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 private Class newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException,
123 IOException {
124
125 String className = properties.getProperty(propertyPrefix + "class");
126 if (className == null) {
127 throw new IOException("Expected property is missing: " + propertyPrefix + "class");
128 }
129 Class clazz = null;
130 ClassLoader loader = Thread.currentThread().getContextClassLoader();
131 if (loader != null) {
132 try {
133 clazz = loader.loadClass(className);
134 } catch (ClassNotFoundException e) {
135 // ignore
136 }
137 }
138 if (clazz == null) {
139 clazz = FactoryFinder.class.getClassLoader().loadClass(className);
140 }
141 return clazz;
142 }
143
144 private Properties doFindFactoryProperies(String key) throws IOException {
145 String uri = path + key;
146
147 // lets try the thread context class loader first
148 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
149 if (classLoader == null) {
150 classLoader = getClass().getClassLoader();
151 }
152 InputStream in = classLoader.getResourceAsStream(uri);
153 if (in == null) {
154 in = FactoryFinder.class.getClassLoader().getResourceAsStream(uri);
155 if (in == null) {
156 throw new NoFactoryAvailableException(uri);
157 }
158 }
159
160 // lets load the file
161 BufferedInputStream reader = null;
162 try {
163 reader = new BufferedInputStream(in);
164 Properties properties = new Properties();
165 properties.load(reader);
166 return properties;
167 } finally {
168 try {
169 reader.close();
170 } catch (Exception ignore) {
171 }
172 }
173 }
174 }