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