| 
					
				 | 
			
			
				@@ -0,0 +1,450 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package cn.nosum.common.extension; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import cn.nosum.common.annotation.Activate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import cn.nosum.common.annotation.Adaptive; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import cn.nosum.common.annotation.DisableInject; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import cn.nosum.common.annotation.SPI; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import cn.nosum.common.util.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.slf4j.Logger; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.slf4j.LoggerFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.io.BufferedReader; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.io.InputStreamReader; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.lang.reflect.Method; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.lang.reflect.Modifier; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.net.URL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.nio.charset.StandardCharsets; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.concurrent.ConcurrentHashMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.concurrent.ConcurrentMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.regex.Pattern; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Load gateway ExtensionLoader 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @param <T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public class ExtensionLoader<T> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final Logger logger= LoggerFactory.getLogger(ExtensionLoader.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 需要扫描的路径 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String GATEWAY_DIRECTORY = "META-INF/gateway/"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 缓存 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Set<Class<?>> cachedWrapperClasses; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final ExtensionFactory objectFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private volatile Class<?> cachedAdaptiveClass = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final Class<?> type; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String cachedDefaultName; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 记录错误信息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private volatile Throwable createAdaptiveInstanceError; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private ExtensionLoader(Class<?> type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.type = type; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @SuppressWarnings("unchecked") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (type == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Extension type == null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!type.isInterface()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!withExtensionAnnotation(type)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Extension type (" + type + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (loader == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return loader; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @SuppressWarnings("unchecked") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public T getExtension(String name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (StringUtils.isEmpty(name)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Extension name == null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ("true".equals(name)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return getDefaultExtension(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        final Holder<Object> holder = getOrCreateHolder(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Object instance = holder.get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (instance == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            synchronized (holder) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                instance = holder.get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (instance == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    instance = createExtension(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    holder.set(instance); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return (T) instance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Holder<Object> getOrCreateHolder(String name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Holder<Object> holder = cachedInstances.get(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (holder == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cachedInstances.putIfAbsent(name, new Holder<>()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            holder = cachedInstances.get(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return holder; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private T createExtension(String name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Class<?> clazz = getExtensionClasses().get(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (clazz == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new NullPointerException(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            T instance = (T) EXTENSION_INSTANCES.get(clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (instance == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                instance = (T) EXTENSION_INSTANCES.get(clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            injectExtension(instance); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Set<Class<?>> wrapperClasses = cachedWrapperClasses; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (CollectionUtils.isNotEmpty(wrapperClasses)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                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 + ") couldn't be instantiated: " + t.getMessage(), t); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 实现依赖注入 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param instance 需要进行依赖注入的实例 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private T injectExtension(T instance) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (objectFactory != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for (Method method : instance.getClass().getMethods()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (isSetter(method)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        // 存在 @DisableInject 则跳过 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (method.getAnnotation(DisableInject.class) != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        Class<?> pt = method.getParameterTypes()[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (ReflectUtils.isPrimitives(pt)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            String property = getSetterProperty(method); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            Object object = objectFactory.getExtension(pt, property); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (object != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                method.invoke(instance, object); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            logger.error("Failed to inject via method " + method.getName() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    + " of interface " + type.getName() + ": " + e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.error(e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return instance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 根据默认扩展名获取到的实例 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public T getDefaultExtension() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        getExtensionClasses(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (StringUtils.isEmpty(cachedDefaultName) || "true".equals(cachedDefaultName)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return getExtension(cachedDefaultName); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 获取自适应扩展点 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @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("Failed to create adaptive instance: " + t.toString(), t); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return (T) instance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @SuppressWarnings("unchecked") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private T createAdaptiveExtension() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return injectExtension((T) getAdaptiveExtensionClass().newInstance()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 获取自适应扩展点 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Class<?> getAdaptiveExtensionClass() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        getExtensionClasses(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (cachedAdaptiveClass != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return cachedAdaptiveClass; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Map<String, Class<?>> getExtensionClasses() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Map<String, Class<?>> classes = cachedClasses.get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (classes == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            synchronized (cachedClasses) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                classes = cachedClasses.get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (classes == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    classes = loadExtensionClasses(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    cachedClasses.set(classes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return classes; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 如果存在默认扩展名,提取并且缓存 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void cacheDefaultExtensionName() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        final SPI defaultAnnotation = type.getAnnotation(SPI.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (defaultAnnotation != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String value = defaultAnnotation.value(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if ((value = value.trim()).length() > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                String[] names = NAME_SEPARATOR.split(value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (names.length > 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    throw new IllegalStateException("More than 1 default extension name on extension " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            + type.getName() + ": " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            + Arrays.toString(names)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (names.length == 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    cachedDefaultName = names[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // synchronized in getExtensionClasses 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Map<String, Class<?>> loadExtensionClasses() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cacheDefaultExtensionName(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Map<String, Class<?>> extensionClasses = new HashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        loadDirectory(extensionClasses, GATEWAY_DIRECTORY, type.getName()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return extensionClasses; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String fileName = dir + type; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Enumeration<URL> 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 resourceURL = urls.nextElement(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    loadResource(extensionClasses, classLoader, resourceURL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Throwable t) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.error("Exception occurred when loading extension class (interface: " + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    type + ", description file: " + fileName + ").", t); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                String line; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                while ((line = reader.readLine()) != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    final int ci = line.indexOf('#'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (ci >= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        line = line.substring(0, ci); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    line = line.trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (line.length() > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            String name = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            int i = line.indexOf('='); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (i > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                name = line.substring(0, i).trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                line = line.substring(i + 1).trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (line.length() > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } catch (Throwable t) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            exceptions.put(line, e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Throwable t) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.error("Exception occurred when loading extension class (interface: " + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    type + ", class file: " + resourceURL + ") in " + resourceURL, t); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * cache name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void cacheName(Class<?> clazz, String name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!cachedNames.containsKey(clazz)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cachedNames.put(clazz, name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!type.isAssignableFrom(clazz)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalStateException("Error occurred when loading extension class (interface: " + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    type + ", class line: " + clazz.getName() + "), class " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    + clazz.getName() + " is not subtype of interface."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (clazz.isAnnotationPresent(Adaptive.class)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cacheAdaptiveClass(clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else if (isWrapperClass(clazz)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cacheWrapperClass(clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            clazz.getConstructor(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (StringUtils.isEmpty(name)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                name = findAnnotationName(clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (name.length() == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String[] names = NAME_SEPARATOR.split(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (ArrayUtils.isNotEmpty(names)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                cacheActivateClass(clazz, names[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for (String n : names) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    cacheName(clazz, n); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    saveInExtensionClass(extensionClasses, clazz, n); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Class<?> c = extensionClasses.get(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (c == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            extensionClasses.put(name, clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else if (c != clazz) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @SuppressWarnings("deprecation") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String findAnnotationName(Class<?> clazz) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cn.nosum.common.annotation.Extension extension = clazz.getAnnotation(cn.nosum.common.annotation.Extension.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (extension == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String name = clazz.getSimpleName(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (name.endsWith(type.getSimpleName())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                name = name.substring(0, name.length() - type.getSimpleName().length()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return name.toLowerCase(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return extension.value(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void cacheActivateClass(Class<?> clazz, String name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Activate activate = clazz.getAnnotation(Activate.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (activate != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cachedActivates.put(name, activate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cn.nosum.common.annotation.Activate oldActivate = clazz.getAnnotation( cn.nosum.common.annotation.Activate.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (oldActivate != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                cachedActivates.put(name, oldActivate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void cacheAdaptiveClass(Class<?> clazz) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        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()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void cacheWrapperClass(Class<?> clazz) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (cachedWrapperClasses == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cachedWrapperClasses = new HashSet<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cachedWrapperClasses.add(clazz); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private boolean isWrapperClass(Class<?> clazz) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            clazz.getConstructor(type); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (NoSuchMethodException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private boolean isSetter(Method method) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return method.getName().startsWith("set") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                && method.getParameterTypes().length == 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                && Modifier.isPublic(method.getModifiers()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static ClassLoader findClassLoader() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return ClassUtils.getClassLoader(ExtensionLoader.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static <T> boolean withExtensionAnnotation(Class<T> type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return type.isAnnotationPresent(SPI.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String getSetterProperty(Method method) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public Set<String> getSupportedExtensions() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Map<String, Class<?>> clazzes = getExtensionClasses(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |