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.util;
018    
019    import java.io.BufferedInputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.util.ArrayList;
023    import java.util.Collections;
024    import java.util.List;
025    import java.util.Properties;
026    import java.util.concurrent.ConcurrentHashMap;
027    
028    import org.apache.camel.spi.Injector;
029    
030    /**
031     * Finder to find factories from the resource classpath, usually <b>META-INF/services/org/apache/camel/</b>.
032     */
033    public class FactoryFinder {
034        protected final ConcurrentHashMap<String, Class> classMap = new ConcurrentHashMap<String, Class>();
035        private final String path;    
036    
037        public FactoryFinder() {
038            this("META-INF/services/org/apache/camel/");
039        }
040    
041        public FactoryFinder(String path) {
042            this.path = path;
043        }
044    
045        /**
046         * Creates a new instance of the given key
047         *
048         * @param key is the key to add to the path to find a text file containing
049         *            the factory name
050         * @return a newly created instance
051         */
052        public Object newInstance(String key) throws IllegalAccessException, InstantiationException, IOException,
053            ClassNotFoundException {
054            return newInstance(key, (String)null);
055        }
056    
057        public Object newInstance(String key, String propertyPrefix) throws IllegalAccessException,
058            InstantiationException, IOException, ClassNotFoundException {
059            Class clazz = findClass(key, propertyPrefix);
060            return clazz.newInstance();
061        }
062    
063        public Object newInstance(String key, Injector injector) throws IOException, ClassNotFoundException {
064            return newInstance(key, injector, (String)null);
065        }
066    
067        public Object newInstance(String key, Injector injector, String propertyPrefix) throws IOException,
068            ClassNotFoundException {
069            Class<?> type = findClass(key, propertyPrefix);
070            return injector.newInstance(type);
071        }
072    
073        public <T> T newInstance(String key, Injector injector, Class<T> expectedType) throws IOException,
074            ClassNotFoundException {
075            return newInstance(key, injector, null, expectedType);
076        }
077    
078        public <T> T newInstance(String key, Injector injector, String propertyPrefix, Class<T> expectedType)
079            throws IOException, ClassNotFoundException {
080            Class<?> type = findClass(key, propertyPrefix);
081            Object value = injector.newInstance(type);
082            if (expectedType.isInstance(value)) {
083                return expectedType.cast(value);
084            } else {
085                throw new ClassCastException("Not instanceof " + expectedType.getName() + " value: " + value);
086            }
087        }
088    
089        public <T> List<T> newInstances(String key, Injector injector, Class<T> type) throws IOException,
090            ClassNotFoundException {
091            List<Class> list = findClasses(key);
092            List<T> answer = new ArrayList<T>(list.size());
093            answer.add(newInstance(key, injector, type));
094            return answer;
095        }
096    
097        public Class findClass(String key) throws ClassNotFoundException, IOException {
098            return findClass(key, null);
099        }
100    
101        public Class findClass(String key, String propertyPrefix) throws ClassNotFoundException, IOException {
102            if (propertyPrefix == null) {
103                propertyPrefix = "";
104            }
105    
106            Class clazz = (Class)classMap.get(propertyPrefix + key);
107            if (clazz == null) {
108                clazz = newInstance(doFindFactoryProperties(key), propertyPrefix);
109                if (clazz != null) {
110                    classMap.put(propertyPrefix + key, clazz);
111                }
112            }
113            return clazz;
114        }
115    
116        public List<Class> findClasses(String key) throws ClassNotFoundException, IOException {
117            return findClasses(key, null);
118        }
119    
120        public List<Class> findClasses(String key, String propertyPrefix) throws ClassNotFoundException,
121            IOException {
122            // TODO change to support finding multiple classes on the classpath!
123            Class type = findClass(key, propertyPrefix);
124            return Collections.singletonList(type);
125        }
126    
127        public String getPath() {
128            return path;
129        }
130    
131        private Class newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException,
132            IOException {
133    
134            String className = properties.getProperty(propertyPrefix + "class");
135            if (className == null) {
136                throw new IOException("Expected property is missing: " + propertyPrefix + "class");
137            }
138    
139            return ObjectHelper.loadClass(className);
140        }
141    
142        private Properties doFindFactoryProperties(String key) throws IOException {
143            String uri = path + key;
144    
145            InputStream in = ObjectHelper.loadResourceAsStream(uri);
146            if (in == null) {
147                throw new NoFactoryAvailableException(uri);
148            }
149    
150            // lets load the file
151            BufferedInputStream reader = null;
152            try {
153                reader = new BufferedInputStream(in);
154                Properties properties = new Properties();
155                properties.load(reader);
156                return properties;
157            } finally {
158                ObjectHelper.close(reader, key, null);
159                ObjectHelper.close(in, key, null);
160            }
161        }
162        
163    }