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        private final String path;
035        private final ConcurrentHashMap classMap = new ConcurrentHashMap();
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                classMap.put(propertyPrefix + key, clazz);
110            }
111            return clazz;
112        }
113    
114        public List<Class> findClasses(String key) throws ClassNotFoundException, IOException {
115            return findClasses(key, null);
116        }
117    
118        public List<Class> findClasses(String key, String propertyPrefix) throws ClassNotFoundException,
119            IOException {
120            // TODO change to support finding multiple classes on the classpath!
121            Class type = findClass(key, propertyPrefix);
122            return Collections.singletonList(type);
123        }
124    
125        public String getPath() {
126            return path;
127        }
128    
129        private Class newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException,
130            IOException {
131    
132            String className = properties.getProperty(propertyPrefix + "class");
133            if (className == null) {
134                throw new IOException("Expected property is missing: " + propertyPrefix + "class");
135            }
136    
137            return ObjectHelper.loadClass(className);
138        }
139    
140        private Properties doFindFactoryProperties(String key) throws IOException {
141            String uri = path + key;
142    
143            InputStream in = ObjectHelper.loadResourceAsStream(uri);
144            if (in == null) {
145                throw new NoFactoryAvailableException(uri);
146            }
147    
148            // lets load the file
149            BufferedInputStream reader = null;
150            try {
151                reader = new BufferedInputStream(in);
152                Properties properties = new Properties();
153                properties.load(reader);
154                return properties;
155            } finally {
156                ObjectHelper.close(reader, key, null);
157                ObjectHelper.close(in, key, null);
158            }
159        }
160        
161    }