All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.fartherp.framework.common.extension.ExtensionLoader Maven / Gradle / Ivy

There is a newer version: 3.0.6
Show newest version
/*
 * Copyright (c) 2017. CK. All rights reserved.
 */
package com.github.fartherp.framework.common.extension;

import com.github.fartherp.framework.common.extension.io.UnsafeStringWriter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

/**
 * framework使用的扩展点获取。

*

    *
  • 自动注入关联扩展点。
  • *
  • 自动Wrap上扩展点的Wrap类。
  • *
  • 缺省获得的的扩展点是一个Adaptive Instance。 *
* * @author CK * @see JDK5.0的自动发现机制实现 */ public class ExtensionLoader { private static final Log logger = LogFactory.getLog(ExtensionLoader.class); private static final String DEFAULT_DIRECTORY = "META-INF/"; private static final String FRAMEWORK_DIRECTORY = DEFAULT_DIRECTORY + "framework"; private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); private static final ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS = new ConcurrentHashMap, ExtensionLoader>(); private static final ConcurrentMap, Object> EXTENSION_INSTANCES = new ConcurrentHashMap, Object>(); // ============================== private final Class type; private final ExtensionFactory objectFactory; private final ConcurrentMap, String> cachedNames = new ConcurrentHashMap, String>(); private final Holder>> cachedClasses = new Holder>>(); private final Map cachedActivates = new ConcurrentHashMap(); private final ConcurrentMap> cachedInstances = new ConcurrentHashMap>(); private final Holder cachedAdaptiveInstance = new Holder(); private volatile Class cachedAdaptiveClass = null; private String cachedDefaultName; private volatile Throwable createAdaptiveInstanceError; private Set> cachedWrapperClasses; private Map exceptions = new ConcurrentHashMap(); private List paths; private ExtensionLoader(Class type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); } private ExtensionLoader(Class type, String path) { this(type); this.addPath(path); } /** * 类上是否有{@link SPI}注解 * @param type Class * @param 类型 * @return true of false */ private static boolean withExtensionAnnotation(Class type) { return type.isAnnotationPresent(SPI.class); } /** *
     *     1: 类型不能为空
     *     2: 类必须是接口
     *     3: 必须有{@link @SPI}注解
     * 
* @param type Class * @param path 扩展自定义目录 * @param 类型 * @return ExtensionLoader */ @SuppressWarnings("unchecked") public static ExtensionLoader getExtensionLoader(Class type, String path) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type); if (loader == null) { ExtensionLoader extensionLoader = new ExtensionLoader(type, path); EXTENSION_LOADERS.putIfAbsent(type, extensionLoader); loader = extensionLoader; } return loader; } /** * 默认目录 framework/ * @param type 类型 * @param Class * @return ExtensionLoader */ public static ExtensionLoader getExtensionLoader(Class type) { return getExtensionLoader(type, null); } private static ClassLoader findClassLoader() { return ExtensionLoader.class.getClassLoader(); } public String getExtensionName(T extensionInstance) { return getExtensionName(extensionInstance.getClass()); } public String getExtensionName(Class extensionClass) { return cachedNames.get(extensionClass); } /** * 返回扩展点实例,如果没有指定的扩展点或是还没加载(即实例化)则返回null。注意:此方法不会触发扩展点的加载。 *

* 一般应该调用{@link #getExtension(String)}方法获得扩展,这个方法会触发扩展点加载。 * * @see #getExtension(String) */ @SuppressWarnings("unchecked") public T getLoadedExtension(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("Extension name == null"); } Holder holder = cachedInstances.get(name); if (holder == null) { Holder objectHolder = new Holder(); cachedInstances.putIfAbsent(name, objectHolder); holder = objectHolder; } return (T) holder.get(); } /** * 返回已经加载的扩展点的名字。 *

* 一般应该调用{@link #getSupportedExtensions()}方法获得扩展,这个方法会返回所有的扩展点。 * * @see #getSupportedExtensions() */ public Set getLoadedExtensions() { return Collections.unmodifiableSet(new TreeSet(cachedInstances.keySet())); } /** * 返回指定名字的扩展。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}. * * @param name * @return */ @SuppressWarnings("unchecked") public T getExtension(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { return getDefaultExtension(); } Holder holder = cachedInstances.get(name); if (holder == null) { Holder objectHolder = new Holder(); cachedInstances.putIfAbsent(name, objectHolder); holder = objectHolder; } Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name); holder.set(instance); } } } return (T) instance; } /** * 返回缺省的扩展,如果没有设置则返回null。 */ public T getDefaultExtension() { getExtensionClasses(); if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); } public boolean hasExtension(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("Extension name == null"); } try { return getExtensionClass(name) != null; } catch (Throwable t) { return false; } } public Set getSupportedExtensions() { Map> clazzes = getExtensionClasses(); return Collections.unmodifiableSet(new TreeSet(clazzes.keySet())); } /** * 返回缺省的扩展点名,如果没有设置缺省则返回null。 */ public String getDefaultExtensionName() { getExtensionClasses(); return cachedDefaultName; } /** * 编程方式添加新扩展点。 * * @param name 扩展点名 * @param clazz 扩展点类 * @throws IllegalStateException 要添加扩展点名已经存在。 */ public void addExtension(String name, Class clazz) { getExtensionClasses(); // load classes if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Input type " + clazz + "not implement Extension " + type); } if (clazz.isInterface()) { throw new IllegalStateException("Input type " + clazz + "can not be interface!"); } if (!clazz.isAnnotationPresent(Adaptive.class)) { if (StringUtils.isBlank(name)) { throw new IllegalStateException("Extension name is blank (Extension " + type + ")!"); } if (cachedClasses.get().containsKey(name)) { throw new IllegalStateException("Extension name " + name + " already existed(Extension " + type + ")!"); } cachedNames.put(clazz, name); cachedClasses.get().put(name, clazz); } else { if (cachedAdaptiveClass != null) { throw new IllegalStateException("Adaptive Extension already existed(Extension " + type + ")!"); } cachedAdaptiveClass = clazz; } } /** * 编程方式添加替换已有扩展点。 * * @param name 扩展点名 * @param clazz 扩展点类 * @throws IllegalStateException 要添加扩展点名已经存在。 * @deprecated 不推荐应用使用,一般只在测试时可以使用 */ @Deprecated public void replaceExtension(String name, Class clazz) { getExtensionClasses(); // load classes if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Input type " + clazz + "not implement Extension " + type); } if (clazz.isInterface()) { throw new IllegalStateException("Input type " + clazz + "can not be interface!"); } if (!clazz.isAnnotationPresent(Adaptive.class)) { if (StringUtils.isBlank(name)) { throw new IllegalStateException("Extension name is blank (Extension " + type + ")!"); } if (!cachedClasses.get().containsKey(name)) { throw new IllegalStateException("Extension name " + name + " not existed(Extension " + type + ")!"); } cachedNames.put(clazz, name); cachedClasses.get().put(name, clazz); cachedInstances.remove(name); } else { if (cachedAdaptiveClass == null) { throw new IllegalStateException("Adaptive Extension not existed(Extension " + type + ")!"); } cachedAdaptiveClass = clazz; cachedAdaptiveInstance.set(null); } } @SuppressWarnings("unchecked") public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; } private IllegalStateException findException(String name) { for (Map.Entry entry : exceptions.entrySet()) { if (entry.getKey().toLowerCase().contains(name.toLowerCase())) { return entry.getValue(); } } StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name); int i = 1; for (Map.Entry entry : exceptions.entrySet()) { if (i == 1) { buf.append(", possible causes: "); } buf.append("\r\n("); buf.append(i++); buf.append(") "); buf.append(entry.getKey()); buf.append(":\r\n"); buf.append(toString(entry.getValue())); } return new IllegalStateException(buf.toString()); } public static String toString(Throwable e) { UnsafeStringWriter w = new UnsafeStringWriter(); PrintWriter p = new PrintWriter(w); p.print(e.getClass().getName()); if (e.getMessage() != null) { p.print(": " + e.getMessage()); } p.println(); try { e.printStackTrace(p); return w.toString(); } finally { p.close(); } } @SuppressWarnings("unchecked") private T createExtension(String name) { Class clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { T t = (T) clazz.newInstance(); EXTENSION_INSTANCES.putIfAbsent(clazz, t); instance = t; } injectExtension(instance); Set> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && wrapperClasses.size() > 0) { for (Class wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); } } private T injectExtension(T instance) { try { if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { Class pt = method.getParameterTypes()[0]; try { String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); if (object != null) { method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; } private Class getExtensionClass(String name) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (name == null) { throw new IllegalArgumentException("Extension name == null"); } Class clazz = getExtensionClasses().get(name); if (clazz == null) { throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + "!"); } return clazz; } private Map> getExtensionClasses() { Map> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } // 此方法已经getExtensionClasses方法同步过。 private Map> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { cachedDefaultName = defaultAnnotation.value(); } Map> extensionClasses = new HashMap>(); loadFile(extensionClasses, FRAMEWORK_DIRECTORY); for (String path : paths) { loadFile(extensionClasses, DEFAULT_DIRECTORY + path); } return extensionClasses; } /** * 加载文件 * @param extensionClasses 扩展类 * @param dir 目录 */ private void loadFile(Map> extensionClasses, String dir) { int diff = dir.length() - dir.lastIndexOf("/"); String fileName = diff == 1 ? dir + type.getName() : dir + "/" + type.getName(); try { Enumeration urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL url = urls.nextElement(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); try { String line = null; while ((line = reader.readLine()) != null) { // 内部类 line: javassist=com.github.fartherp.framework.common.compiler.support.JavassistCompiler#Test final int ci = line.indexOf('#'); if (ci >= 0) { // line: javassist=com.github.fartherp.framework.common.compiler.support.JavassistCompiler line = line.substring(0, ci).trim(); } if (line.length() > 0) { try { String name = null; int i = line.indexOf('='); if (i > 0) { // name: javassist name = line.substring(0, i).trim(); // line: com.github.fartherp.framework.common.compiler.support.JavassistCompiler line = line.substring(i + 1).trim(); } if (line.length() > 0) { Class clazz = Class.forName(line, true, classLoader); if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error when load extension class(interface: " + type + ", class: " + clazz.getName() + "), class is not subtype of interface."); } // 类有@Adaptive注解,只需有一个@Adaptive,适配 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } else { try { clazz.getConstructor(type); Set> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { clazz.getConstructor(); // if (name == null || name.length() == 0) { // name = findAnnotationName(clazz); // if (name == null || name.length() == 0) { // if (clazz.getSimpleName().length() > type.getSimpleName().length() // && clazz.getSimpleName().endsWith(type.getSimpleName())) { // name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); // } else { // throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); // } // } // } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } for (String n : names) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class c = extensionClasses.get(n); if (c == null) { extensionClasses.put(n, clazz); } else if (c != clazz) { throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); } } } } } } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } // end of while read lines } finally { reader.close(); } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + url + ") in " + url, t); } } // end of while urls } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } } /** * 创建适配的扩展对象 * @return obj */ @SuppressWarnings("unchecked") private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); } } /** * 获取适配扩展类型 * @return Class */ private Class getAdaptiveExtensionClass() { getExtensionClasses(); return cachedAdaptiveClass; } @Override public String toString() { return this.getClass().getName() + "[" + type.getName() + "]"; } public static boolean isNotEmpty(String value) { return !isEmpty(value); } public static boolean isEmpty(String value) { return StringUtils.isBlank(value) || "false".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value) || "null".equalsIgnoreCase(value) || "N/A".equalsIgnoreCase(value); } public void addPath(String path) { if (this.paths == null) { this.paths = new ArrayList(); } this.paths.add(path); } }