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