001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.properties;
018    
019    import java.io.FileInputStream;
020    import java.io.FileNotFoundException;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.util.Map;
024    import java.util.Properties;
025    
026    import org.apache.camel.CamelContext;
027    import org.apache.camel.util.IOHelper;
028    import org.apache.camel.util.ObjectHelper;
029    
030    /**
031     * Default {@link org.apache.camel.component.properties.PropertiesResolver} which can resolve properties
032     * from file and classpath.
033     * <p/>
034     * You can denote <tt>classpath:</tt> or <tt>file:</tt> as prefix in the uri to select whether the file
035     * is located in the classpath or on the file system.
036     *
037     * @version 
038     */
039    public class DefaultPropertiesResolver implements PropertiesResolver {
040    
041        public Properties resolveProperties(CamelContext context, boolean ignoreMissingLocation, String... uri) throws Exception {
042            Properties answer = new Properties();
043    
044            for (String path : uri) {
045                if (path.startsWith("ref:")) {
046                    Properties prop = loadPropertiesFromRegistry(context, ignoreMissingLocation, path);
047                    prop = prepareLoadedProperties(prop);
048                    answer.putAll(prop);
049                } else if (path.startsWith("file:")) {
050                    Properties prop = loadPropertiesFromFilePath(context, ignoreMissingLocation, path);
051                    prop = prepareLoadedProperties(prop);
052                    answer.putAll(prop);
053                } else {
054                    // default to classpath
055                    Properties prop = loadPropertiesFromClasspath(context, ignoreMissingLocation, path);
056                    prop = prepareLoadedProperties(prop);
057                    answer.putAll(prop);
058                }
059            }
060    
061            return answer;
062        }
063    
064        protected Properties loadPropertiesFromFilePath(CamelContext context, boolean ignoreMissingLocation, String path) throws IOException {
065            Properties answer = new Properties();
066    
067            if (path.startsWith("file:")) {
068                path = ObjectHelper.after(path, "file:");
069            }
070    
071            InputStream is = null;
072            try {
073                is = new FileInputStream(path);
074                answer.load(is);
075            } catch (FileNotFoundException e) {
076                if (!ignoreMissingLocation) {
077                    throw e;
078                }
079            } finally {
080                IOHelper.close(is);
081            }
082    
083            return answer;
084        }
085    
086        protected Properties loadPropertiesFromClasspath(CamelContext context, boolean ignoreMissingLocation, String path) throws IOException {
087            Properties answer = new Properties();
088    
089            if (path.startsWith("classpath:")) {
090                path = ObjectHelper.after(path, "classpath:");
091            }
092    
093            InputStream is = context.getClassResolver().loadResourceAsStream(path);
094            if (is == null) {
095                if (!ignoreMissingLocation) {
096                    throw new FileNotFoundException("Properties file " + path + " not found in classpath");
097                }
098            } else {
099                try {
100                    answer.load(is);
101                } finally {
102                    IOHelper.close(is);
103                }
104            }
105            return answer;
106        }
107    
108        @SuppressWarnings({"rawtypes", "unchecked"})
109        protected Properties loadPropertiesFromRegistry(CamelContext context, boolean ignoreMissingLocation, String path) throws IOException {
110            if (path.startsWith("ref:")) {
111                path = ObjectHelper.after(path, "ref:");
112            }
113            Properties answer;
114            try {
115                answer = context.getRegistry().lookupByNameAndType(path, Properties.class);
116            } catch (Exception ex) {
117                // just look up the Map as a fault back
118                Map map = context.getRegistry().lookupByNameAndType(path, Map.class);
119                answer = new Properties();
120                answer.putAll(map);
121            }
122            if (answer == null && (!ignoreMissingLocation)) {
123                throw new FileNotFoundException("Properties " + path + " not found in registry");
124            }
125            return answer != null ? answer : new Properties();
126        }
127    
128        /**
129         * Strategy to prepare loaded properties before being used by Camel.
130         * <p/>
131         * This implementation will ensure values are trimmed, as loading properties from
132         * a file with values having trailing spaces is not automatic trimmed by the Properties API
133         * from the JDK.
134         *
135         * @param properties  the properties
136         * @return the prepared properties
137         */
138        protected Properties prepareLoadedProperties(Properties properties) {
139            Properties answer = new Properties();
140            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
141                Object key = entry.getKey();
142                Object value = entry.getValue();
143                if (value instanceof String) {
144                    String s = (String) value;
145    
146                    // trim any trailing spaces which can be a problem when loading from
147                    // a properties file, note that java.util.Properties does already this
148                    // for any potential leading spaces so there's nothing to do there
149                    value = trimTrailingWhitespaces(s);
150                }
151                answer.put(key, value);
152            }
153            return answer;
154        }
155    
156        private static String trimTrailingWhitespaces(String s) {
157            int endIndex = s.length();
158            for (int index = s.length() - 1; index >= 0; index--) {
159                if (s.charAt(index) == ' ') {
160                    endIndex = index;
161                } else {
162                    break;
163                }
164            }
165            String answer = s.substring(0, endIndex);
166            return answer;
167        }
168    
169    }