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 }