001/** 002 * Copyright (C) 2006-2024 Talend Inc. - www.talend.com 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.talend.sdk.component.runtime.manager.service; 017 018import static java.util.Collections.emptyMap; 019import static java.util.stream.Collectors.toList; 020 021import java.io.ObjectStreamException; 022import java.io.Serializable; 023import java.lang.reflect.Field; 024import java.lang.reflect.Modifier; 025import java.lang.reflect.ParameterizedType; 026import java.lang.reflect.Type; 027import java.util.Collection; 028import java.util.Map; 029import java.util.function.Supplier; 030import java.util.stream.Stream; 031 032import org.talend.sdk.component.api.service.Service; 033import org.talend.sdk.component.api.service.configuration.Configuration; 034import org.talend.sdk.component.api.service.injector.Injector; 035import org.talend.sdk.component.runtime.manager.asm.ProxyGenerator; 036import org.talend.sdk.component.runtime.manager.reflect.ReflectionService; 037import org.talend.sdk.component.runtime.serialization.SerializableService; 038 039import lombok.AllArgsConstructor; 040 041// internal service for now, we can refactor it later to expose it if needed 042@AllArgsConstructor 043public class InjectorImpl implements Serializable, Injector { 044 045 private final String plugin; 046 047 private final ReflectionService reflectionService; 048 049 private final ProxyGenerator proxyGenerator; 050 051 private final Map<Class<?>, Object> services; 052 053 @Override 054 public <T> T inject(final T instance) { 055 if (instance == null) { 056 return null; 057 } 058 doInject(instance.getClass(), unwrap(instance)); 059 return instance; 060 } 061 062 private Object unwrap(final Object instance) { 063 if (instance.getClass().getName().endsWith("$$TalendServiceProxy")) { 064 try { 065 return proxyGenerator.getHandler(instance).getDelegate(); 066 } catch (final IllegalStateException nsfe) { 067 // no-op 068 } 069 } 070 return instance; 071 } 072 073 private <T> void doInject(final Class<?> type, final T instance) { 074 if (type == Object.class || type == null) { 075 return; 076 } 077 final Field[] fields = type.getDeclaredFields(); 078 Stream 079 .of(fields) 080 .filter(field -> !Modifier.isStatic(field.getModifiers())) 081 .filter(field -> field.isAnnotationPresent(Service.class)) 082 .peek(f -> { 083 if (!f.isAccessible()) { 084 f.setAccessible(true); 085 } 086 }) 087 .forEach(field -> { 088 Object value = services.get(field.getType()); 089 if (value == null && ParameterizedType.class.isInstance(field.getGenericType())) { 090 final ParameterizedType pt = ParameterizedType.class.cast(field.getGenericType()); 091 if (Class.class.isInstance(pt.getRawType()) 092 && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType()))) { 093 final Type serviceType = pt.getActualTypeArguments()[0]; 094 if (Class.class.isInstance(serviceType)) { 095 final Class<?> serviceClass = Class.class.cast(serviceType); 096 value = services 097 .entrySet() 098 .stream() 099 .filter(e -> serviceClass.isAssignableFrom(e.getKey())) 100 .collect(toList()); 101 } 102 } 103 } 104 if (value != null) { 105 try { 106 field.set(instance, value); 107 } catch (final IllegalAccessException e) { 108 throw new IllegalArgumentException(e); 109 } 110 } 111 }); 112 Stream 113 .of(fields) 114 .filter(field -> !Modifier.isStatic(field.getModifiers())) 115 .filter(field -> field.isAnnotationPresent(Configuration.class)) 116 .peek(field -> { 117 if (Supplier.class != field.getType() 118 || !ParameterizedType.class.isInstance(field.getGenericType())) { 119 throw new IllegalArgumentException("Field " + field + " is not a Supplier<X>,\n" 120 + "it will not be injected otherwise it wouldn't be up to date,\n" 121 + "did you mean Supplier<" + field.getType() + "> ?"); 122 } 123 }) 124 .peek(f -> { 125 if (!f.isAccessible()) { 126 f.setAccessible(true); 127 } 128 }) 129 .forEach(field -> { 130 try { 131 final Class<?> configClass = Class.class 132 .cast(ParameterizedType.class.cast(field.getGenericType()).getActualTypeArguments()[0]); 133 final ClassLoader loader = Thread.currentThread().getContextClassLoader(); 134 final Supplier<?> supplier = () -> { 135 try { 136 return reflectionService 137 .createConfigFactory(services, loader, 138 reflectionService.createContextualSupplier(loader), field.getName(), 139 field.getAnnotation(Configuration.class), field.getAnnotations(), 140 configClass) 141 .apply(emptyMap()); 142 } catch (final NoSuchMethodException e) { 143 throw new IllegalStateException(e); 144 } 145 }; 146 field.set(instance, supplier); 147 } catch (final IllegalAccessException e) { 148 throw new IllegalArgumentException(e); 149 } 150 }); 151 if (type.getSuperclass() != type) { 152 doInject(type.getSuperclass(), instance); 153 } 154 } 155 156 Object writeReplace() throws ObjectStreamException { 157 return new SerializableService(plugin, Injector.class.getName()); 158 } 159}