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