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