package org.talend.dataprep.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.talend.dataprep.JarUrlClassLoader;
import org.talend.dataprep.api.action.Action;
import org.talend.dataprep.exception.TDPException;
import org.talend.dataprep.exception.error.CommonErrorCodes;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class PlugActionImport implements ImportBeanDefinitionRegistrar {
/** logger */
private static final Logger LOGGER = LoggerFactory.getLogger(PlugActionImport.class);
public static final String PREFIX = "action#";
/** Action 插件位置*/
@Value("${action.plug.path}")
private String actionPlugPath;
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
List<String> jarPath = jarPath();
GenericApplicationContext context = new GenericApplicationContext();
JarUrlClassLoader loader = getLoader();
if(StringUtils.isEmpty(jarPath)){
LOGGER.error("please config a absolute path for action plug jar. exp: /data/java/***");
return;
}
context.setClassLoader(loader);
PlugActionDefinitionScanner scanner = new PlugActionDefinitionScanner(context);
AtomicInteger actionCount = new AtomicInteger();
scanner.setBeanNameGenerator((definition, beanRegistry) -> {
try {
final Class<?> clazz = loader.loadClass(definition.getBeanClassName());
Action value = AnnotationUtils.findAnnotation(clazz, Action.class);
if (value!=null) {
return PREFIX + value.value();
}
return definition.getBeanClassName();
} catch (Exception e) {
// Rather unexpected, filter must have found and check the class earlier.
throw new TDPException(CommonErrorCodes.UNEXPECTED_EXCEPTION, e);
} finally {
actionCount.incrementAndGet();
}
});
scanner.addIncludeFilter(new ActionFilter());
scanner.scan(jarPath);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name: beanDefinitionNames){
BeanDefinition definition = context.getBeanDefinition(name);
beanDefinitionRegistry.registerBeanDefinition(name, definition);
}
// 重要! 在此处设置外加Bean的ClassLoader
((DefaultListableBeanFactory) beanDefinitionRegistry).setBeanClassLoader(loader);
LOGGER.info("{} plug action(s) found.", actionCount.get());
}
private class ActionFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
final ClassMetadata classMetadata = metadataReader.getClassMetadata();
JarUrlClassLoader loader = getLoader();
if(loader == null){
return false;
}
try {
final Class<?> clazz = loader.loadClass(classMetadata.getClassName());
return AnnotationUtils.findAnnotation(clazz, Action.class) != null;
} catch (Throwable e) { // NOSONAR
if (!LOGGER.isDebugEnabled()) {
LOGGER.error("Unable to filter class {}.", classMetadata.getClassName());
} else {
LOGGER.debug("Unable to filter class {}.", classMetadata.getClassName(), e);
}
}
return false;
}
}
public JarUrlClassLoader getLoader() {
List<String> jars = jarPath();
if(jars == null || jars.isEmpty()){
return null;
}
URL[] urls = new URL[jars.size()];
for (int i = 0; i < urls.length; i++) {
try {
urls[i] = new URL("file", null, jars.get(i));
}catch (MalformedURLException e){
e.printStackTrace();
}
}
ClassLoader classLoader = this.getClass().getClassLoader();
JarUrlClassLoader loader = new JarUrlClassLoader(urls, classLoader);
return loader;
}
private List<String> jarPath(){
String jarPath = actionPlugPath;
try {
if (jarPath == null){
//从指定配置文件读取数据
LOGGER.warn("Action plug path is null, finding it from properties file...");
Properties properties = getProperties();
jarPath = properties.getProperty("action.plug.path");
}
/*--获取japPath路径下 所有jar包 的绝对路径--*/
File file = new File(jarPath);
if (file.isDirectory()) {
File[] files = file.listFiles();
List<String> jars = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
if (!files[i].isDirectory() && files[i].getName().endsWith(".jar")) {
jars.add(files[i].getAbsolutePath());
}
}
return jars;
}else {
return null;
}
} catch (Exception e) {
LOGGER.error("Not found properties file, please check it");
LOGGER.error(e.getMessage());
return null;
}
}
private Properties getProperties()throws Exception {
String resourceLocation = "etc/application.properties";
String absolutePath = ResourceUtils.getFile(resourceLocation).getAbsolutePath();
LOGGER.info("Properties file location: {}", absolutePath);
InputStream inputStream = new FileInputStream(absolutePath);
Properties properties = new Properties();
if (!Objects.isNull(inputStream)) {
properties.load(inputStream);
}
return properties;
}
}
@Component加上这个注解试一下
你是说这个吗?${action.plug.path}需要在yml配置文件里加上啊