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 */ 017package org.apache.camel.impl; 018 019import java.io.BufferedInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.List; 025import java.util.Properties; 026import java.util.concurrent.ConcurrentHashMap; 027import java.util.concurrent.ConcurrentMap; 028import java.util.function.Function; 029 030import org.apache.camel.NoFactoryAvailableException; 031import org.apache.camel.spi.ClassResolver; 032import org.apache.camel.spi.FactoryFinder; 033import org.apache.camel.spi.Injector; 034import org.apache.camel.util.CastUtils; 035import org.apache.camel.util.IOHelper; 036 037/** 038 * Default factory finder. 039 */ 040public class DefaultFactoryFinder implements FactoryFinder { 041 042 private final ConcurrentMap<String, Class<?>> classMap = new ConcurrentHashMap<>(); 043 private final ConcurrentMap<String, Boolean> classesNotFound = new ConcurrentHashMap<>(); 044 private final ConcurrentMap<String, Exception> classesNotFoundExceptions = new ConcurrentHashMap<>(); 045 private final ClassResolver classResolver; 046 private final String path; 047 048 public DefaultFactoryFinder(ClassResolver classResolver, String resourcePath) { 049 this.classResolver = classResolver; 050 this.path = resourcePath; 051 } 052 053 @Override 054 public String getResourcePath() { 055 return path; 056 } 057 058 @Override 059 public Object newInstance(String key) throws NoFactoryAvailableException { 060 try { 061 return newInstance(key, null); 062 } catch (Exception e) { 063 throw new NoFactoryAvailableException(key, e); 064 } 065 } 066 067 @Override 068 public <T> List<T> newInstances(String key, Injector injector, Class<T> type) throws ClassNotFoundException, IOException { 069 List<Class<T>> list = CastUtils.cast(findClasses(key)); 070 List<T> answer = new ArrayList<>(list.size()); 071 answer.add(newInstance(key, injector, type)); 072 return answer; 073 } 074 075 @Override 076 public Class<?> findClass(String key) throws ClassNotFoundException, IOException { 077 return findClass(key, null); 078 } 079 080 @Override 081 public Class<?> findClass(String key, String propertyPrefix) throws ClassNotFoundException, IOException { 082 final String prefix = propertyPrefix != null ? propertyPrefix : ""; 083 final String classKey = prefix + key; 084 085 return addToClassMap(classKey, new ClassSupplier() { 086 @Override 087 public Class<?> get() throws ClassNotFoundException, IOException { 088 return DefaultFactoryFinder.this.newInstance(DefaultFactoryFinder.this.doFindFactoryProperties(key), prefix); 089 } 090 }); 091 } 092 093 @Override 094 public Class<?> findClass(String key, String propertyPrefix, Class<?> clazz) throws ClassNotFoundException, IOException { 095 // Just ignore clazz which is only useful for OSGiFactoryFinder 096 return findClass(key, propertyPrefix); 097 } 098 099 private Object newInstance(String key, String propertyPrefix) throws IllegalAccessException, 100 InstantiationException, IOException, ClassNotFoundException { 101 Class<?> clazz = findClass(key, propertyPrefix); 102 return clazz.newInstance(); 103 } 104 105 private <T> T newInstance(String key, Injector injector, Class<T> expectedType) throws IOException, 106 ClassNotFoundException { 107 return newInstance(key, injector, null, expectedType); 108 } 109 110 private <T> T newInstance(String key, Injector injector, String propertyPrefix, Class<T> expectedType) 111 throws IOException, ClassNotFoundException { 112 Class<?> type = findClass(key, propertyPrefix); 113 Object value = injector.newInstance(type); 114 if (expectedType.isInstance(value)) { 115 return expectedType.cast(value); 116 } else { 117 throw new ClassCastException("Not instanceof " + expectedType.getName() + " value: " + value); 118 } 119 } 120 121 private List<Class<?>> findClasses(String key) throws ClassNotFoundException, IOException { 122 return findClasses(key, null); 123 } 124 125 private List<Class<?>> findClasses(String key, String propertyPrefix) throws ClassNotFoundException, IOException { 126 Class<?> type = findClass(key, propertyPrefix); 127 return Collections.<Class<?>>singletonList(type); 128 } 129 130 private Class<?> newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException, IOException { 131 String className = properties.getProperty(propertyPrefix + "class"); 132 if (className == null) { 133 throw new IOException("Expected property is missing: " + propertyPrefix + "class"); 134 } 135 136 Class<?> clazz = classResolver.resolveClass(className); 137 if (clazz == null) { 138 throw new ClassNotFoundException(className); 139 } 140 return clazz; 141 } 142 143 private Properties doFindFactoryProperties(String key) throws IOException { 144 String uri = path + key; 145 146 InputStream in = classResolver.loadResourceAsStream(uri); 147 if (in == null) { 148 throw new NoFactoryAvailableException(uri); 149 } 150 151 // lets load the file 152 BufferedInputStream reader = null; 153 try { 154 reader = IOHelper.buffered(in); 155 Properties properties = new Properties(); 156 properties.load(reader); 157 return properties; 158 } finally { 159 IOHelper.close(reader, key, null); 160 IOHelper.close(in, key, null); 161 } 162 } 163 164 /* 165 * This is a wrapper function to deal with exceptions in lambdas: the exception 166 * is wrapped by a runtime exception (WrappedRuntimeException) which we catch 167 * later on with the only purpose to re-throw the original exception. 168 */ 169 protected Class<?> addToClassMap(String key, ClassSupplier mappingFunction) throws ClassNotFoundException, IOException { 170 try { 171 if (classesNotFoundExceptions.containsKey(key) || classesNotFound.containsKey(key)) { 172 Exception e = classesNotFoundExceptions.get(key); 173 if (e == null) { 174 return null; 175 } else { 176 throw new WrappedRuntimeException(e); 177 } 178 } 179 180 Class<?> suppliedClass = classMap.computeIfAbsent(key, new Function<String, Class<?>>() { 181 @Override 182 public Class<?> apply(String classKey) { 183 try { 184 return mappingFunction.get(); 185 } catch (ClassNotFoundException e) { 186 classesNotFoundExceptions.put(key, e); 187 throw new WrappedRuntimeException(e); 188 } catch (NoFactoryAvailableException e) { 189 classesNotFoundExceptions.put(key, e); 190 throw new WrappedRuntimeException(e); 191 } catch (IOException e) { 192 throw new WrappedRuntimeException(e); 193 } 194 } 195 }); 196 197 if (suppliedClass == null) { 198 // mark the key as non-resolvable to prevent pointless searching 199 classesNotFound.put(key, Boolean.TRUE); 200 } 201 202 return suppliedClass; 203 } catch (WrappedRuntimeException e) { 204 if (e.getCause() instanceof ClassNotFoundException) { 205 throw (ClassNotFoundException)e.getCause(); 206 } else if (e.getCause() instanceof NoFactoryAvailableException) { 207 throw (NoFactoryAvailableException)e.getCause(); 208 } else if (e.getCause() instanceof IOException) { 209 throw (IOException)e.getCause(); 210 } else { 211 throw new RuntimeException(e.getCause()); 212 } 213 } 214 } 215 216 @FunctionalInterface 217 protected interface ClassSupplier { 218 Class<?> get() throws ClassNotFoundException, IOException; 219 } 220 221 private final class WrappedRuntimeException extends RuntimeException { 222 WrappedRuntimeException(Exception e) { 223 super(e); 224 } 225 } 226}