This commit is contained in:
parent
5db4dbeba5
commit
10a11e2962
@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright © 2020 xrv <xrg@live.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.github.ehlxr.did.extension;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activate
|
|
||||||
* <p/>
|
|
||||||
* 对于可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。
|
|
||||||
* 比如,过滤扩展,有多个实现,使用Activate Annotation的扩展可以根据条件被自动加载。
|
|
||||||
* <p>
|
|
||||||
* <p/>
|
|
||||||
* 底层框架SPI提供者通过{@link ExtensionLoader}的{@link ExtensionLoader#getActivateExtensions}方法
|
|
||||||
* 获得条件的扩展。
|
|
||||||
*
|
|
||||||
* @see SPI
|
|
||||||
* @see ExtensionLoader
|
|
||||||
*/
|
|
||||||
@Documented
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
|
||||||
public @interface Activate {
|
|
||||||
/**
|
|
||||||
* Group过滤条件。
|
|
||||||
* <br />
|
|
||||||
* 包含{@link ExtensionLoader#getActivateExtensions}的group参数给的值,则返回扩展。
|
|
||||||
* <br />
|
|
||||||
* 如没有Group设置,则不过滤。
|
|
||||||
*/
|
|
||||||
String[] group() default {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Key过滤条件。包含{@link ExtensionLoader#getActivateExtensions}的URL的参数Key中有,则返回扩展。
|
|
||||||
* <p/>
|
|
||||||
* 示例:<br/>
|
|
||||||
* 注解的值 <code>@Activate("cache,validatioin")</code>,
|
|
||||||
* 则{@link ExtensionLoader#getActivateExtensions}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
|
|
||||||
* <br/>
|
|
||||||
* 如没有设置,则不过滤。
|
|
||||||
*/
|
|
||||||
String[] value() default {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序信息,可以不提供。
|
|
||||||
*/
|
|
||||||
String[] before() default {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序信息,可以不提供。
|
|
||||||
*/
|
|
||||||
String[] after() default {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序信息,可以不提供。
|
|
||||||
*/
|
|
||||||
int order() default 0;
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright © 2020 xrv <xrg@live.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.github.ehlxr.did.extension;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Order Comparetor
|
|
||||||
*
|
|
||||||
* @author ehlxr
|
|
||||||
*/
|
|
||||||
public class ActivateComparator implements Comparator<Object> {
|
|
||||||
public static final Comparator<Object> COMPARATOR = new ActivateComparator();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(Object o1, Object o2) {
|
|
||||||
if (o1 == null && o2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (o1 == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (o2 == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (o1.equals(o2)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Activate a1 = o1.getClass().getAnnotation(Activate.class);
|
|
||||||
Activate a2 = o2.getClass().getAnnotation(Activate.class);
|
|
||||||
if ((a1.before().length > 0 || a1.after().length > 0
|
|
||||||
|| a2.before().length > 0 || a2.after().length > 0)
|
|
||||||
&& o1.getClass().getInterfaces().length > 0
|
|
||||||
&& o1.getClass().getInterfaces()[0].isAnnotationPresent(SPI.class)) {
|
|
||||||
ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(o1.getClass().getInterfaces()[0]);
|
|
||||||
if (a1.before().length > 0 || a1.after().length > 0) {
|
|
||||||
String n2 = extensionLoader.getExtensionName(o2.getClass());
|
|
||||||
for (String before : a1.before()) {
|
|
||||||
if (before.equals(n2)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (String after : a1.after()) {
|
|
||||||
if (after.equals(n2)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (a2.before().length > 0 || a2.after().length > 0) {
|
|
||||||
String n1 = extensionLoader.getExtensionName(o1.getClass());
|
|
||||||
for (String before : a2.before()) {
|
|
||||||
if (before.equals(n1)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (String after : a2.after()) {
|
|
||||||
if (after.equals(n1)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int n1 = a1.order();
|
|
||||||
int n2 = a2 == null ? 0 : a2.order();
|
|
||||||
return n1 > n2 ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright © 2020 xrv <xrg@live.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.github.ehlxr.did.extension;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为{@link ExtensionLoader}提供信息。
|
|
||||||
* 使用上类似于工厂方法
|
|
||||||
*
|
|
||||||
* @see ExtensionLoader
|
|
||||||
*/
|
|
||||||
@Documented
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
|
||||||
public @interface Adaptive {
|
|
||||||
String[] value() default {};
|
|
||||||
}
|
|
@ -32,7 +32,6 @@ import java.io.BufferedReader;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -41,18 +40,16 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载和管理扩展。
|
* 加载和管理扩展(简化版 Dubbo SPI)
|
||||||
* <p/>
|
*
|
||||||
* <ul>
|
* @author ehlxr
|
||||||
* <li>管理的扩展实例是<b>单例</b>。
|
|
||||||
* <li>Wrapper实例每次获得扩展实例重新创建,并Wrap到扩展实例上。
|
|
||||||
* </ul>
|
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class ExtensionLoader<T> {
|
public class ExtensionLoader<T> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
|
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
|
||||||
|
|
||||||
private static final String DUBBO_DIRECTORY = "META-INF/extensions/";
|
private static final String EXTENSIONS_DIRECTORY = "META-INF/extensions/";
|
||||||
private static final String DUBBO_INTERNAL_DIRECTORY = "META-INF/extensions/internal/";
|
private static final String EXTENSIONS_INTERNAL_DIRECTORY = "META-INF/extensions/internal/";
|
||||||
|
|
||||||
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*,+\\s*");
|
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*,+\\s*");
|
||||||
|
|
||||||
@ -63,25 +60,21 @@ public class ExtensionLoader<T> {
|
|||||||
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
|
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
|
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
|
||||||
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
|
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
|
||||||
private final Map<Class<?>, List<Object>> cachedActivates = new ConcurrentHashMap<>();
|
|
||||||
private final Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
|
private final Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
|
||||||
private String defaultExtension;
|
private String defaultExtension;
|
||||||
private Set<Class<?>> cachedWrapperClasses;
|
|
||||||
private volatile Class<?> adaptiveClass = null;
|
|
||||||
|
|
||||||
private ExtensionLoader(Class<T> type) {
|
private ExtensionLoader(Class<T> type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ExtensionLoader}的工厂方法。
|
* {@link ExtensionLoader} 的工厂方法。
|
||||||
*
|
*
|
||||||
* @param type 扩展点接口类型
|
* @param type 扩展点接口类型
|
||||||
* @param <T> 扩展点类型
|
* @param <T> 扩展点类型
|
||||||
* @return {@link ExtensionLoader}实例
|
* @return {@link ExtensionLoader} 实例
|
||||||
* @throws IllegalArgumentException 参数为<code>null</code>;
|
* @throws IllegalArgumentException 参数为 <code>null</code>;
|
||||||
* 或是扩展点接口上没有{@link SPI}注解。
|
* 或是扩展点接口上没有 {@link SPI} 注解。
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
|
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
|
||||||
@ -91,7 +84,7 @@ public class ExtensionLoader<T> {
|
|||||||
if (!type.isInterface()) {
|
if (!type.isInterface()) {
|
||||||
throw new IllegalArgumentException("SPI type(" + type.getName() + ") is not interface!");
|
throw new IllegalArgumentException("SPI type(" + type.getName() + ") is not interface!");
|
||||||
}
|
}
|
||||||
if (!withExtensionAnnotation(type)) {
|
if (!type.isAnnotationPresent(SPI.class)) {
|
||||||
throw new IllegalArgumentException("type(" + type.getName() +
|
throw new IllegalArgumentException("type(" + type.getName() +
|
||||||
") is not a extension, because WITHOUT @SPI Annotation!");
|
") is not a extension, because WITHOUT @SPI Annotation!");
|
||||||
}
|
}
|
||||||
@ -104,10 +97,6 @@ public class ExtensionLoader<T> {
|
|||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> boolean withExtensionAnnotation(Class<T> type) {
|
|
||||||
return type.isAnnotationPresent(SPI.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the String of Throwable, like the output of {@link Throwable#printStackTrace()}.
|
* Get the String of Throwable, like the output of {@link Throwable#printStackTrace()}.
|
||||||
*
|
*
|
||||||
@ -121,10 +110,6 @@ public class ExtensionLoader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T getExtension(Class<T> clazz, String value) {
|
|
||||||
return getExtensionLoader(clazz).getExtension(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getExtension(String name) {
|
public T getExtension(String name) {
|
||||||
if (StringUtil.isNullOrEmpty(name)) {
|
if (StringUtil.isNullOrEmpty(name)) {
|
||||||
throw new IllegalArgumentException("SPI name == null");
|
throw new IllegalArgumentException("SPI name == null");
|
||||||
@ -146,6 +131,7 @@ public class ExtensionLoader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//noinspection unchecked
|
||||||
return (T) instance;
|
return (T) instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +139,6 @@ public class ExtensionLoader<T> {
|
|||||||
* 返回缺省的扩展。
|
* 返回缺省的扩展。
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException 指定的扩展没有设置缺省扩展点
|
* @throws IllegalStateException 指定的扩展没有设置缺省扩展点
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
*/
|
||||||
public T getDefaultExtension() {
|
public T getDefaultExtension() {
|
||||||
loadExtensionClasses0();
|
loadExtensionClasses0();
|
||||||
@ -174,21 +159,6 @@ public class ExtensionLoader<T> {
|
|||||||
return Collections.unmodifiableSet(new HashSet<>(classes.keySet()));
|
return Collections.unmodifiableSet(new HashSet<>(classes.keySet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* get active extension points
|
|
||||||
*
|
|
||||||
* @return extension list which are activated.
|
|
||||||
*/
|
|
||||||
public List<T> getActivateExtensions() {
|
|
||||||
getExtensionClasses();
|
|
||||||
|
|
||||||
// TODO {group value} filter
|
|
||||||
|
|
||||||
List<T> activates = (List<T>) cachedActivates.get(type);
|
|
||||||
activates.sort(ActivateComparator.COMPARATOR);
|
|
||||||
return activates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExtensionName(Class<?> spi) {
|
public String getExtensionName(Class<?> spi) {
|
||||||
getExtensionClasses();
|
getExtensionClasses();
|
||||||
return cachedNames.get(spi);
|
return cachedNames.get(spi);
|
||||||
@ -200,16 +170,14 @@ public class ExtensionLoader<T> {
|
|||||||
throw findExtensionClassLoadException(name);
|
throw findExtensionClassLoadException(name);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
//noinspection unchecked
|
||||||
T instance = (T) EXTENSION_INSTANCES.get(clazz);
|
T instance = (T) EXTENSION_INSTANCES.get(clazz);
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
|
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
|
||||||
|
//noinspection unchecked
|
||||||
instance = (T) EXTENSION_INSTANCES.get(clazz);
|
instance = (T) EXTENSION_INSTANCES.get(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 支持组件自动注入
|
|
||||||
|
|
||||||
// TODO 支持wrapper类包装实现aop相似的功能
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throw new IllegalStateException("SPI instance(name: " + name + ", class: " +
|
throw new IllegalStateException("SPI instance(name: " + name + ", class: " +
|
||||||
@ -217,9 +185,6 @@ public class ExtensionLoader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread-safe.
|
|
||||||
*/
|
|
||||||
private Map<String, Class<?>> getExtensionClasses() {
|
private Map<String, Class<?>> getExtensionClasses() {
|
||||||
Map<String, Class<?>> classes = cachedClasses.get();
|
Map<String, Class<?>> classes = cachedClasses.get();
|
||||||
if (classes == null) {
|
if (classes == null) {
|
||||||
@ -275,8 +240,8 @@ public class ExtensionLoader<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Class<?>> extensionClasses = new HashMap<>();
|
Map<String, Class<?>> extensionClasses = new HashMap<>();
|
||||||
loadFile(extensionClasses, DUBBO_DIRECTORY);
|
loadFile(extensionClasses, EXTENSIONS_DIRECTORY);
|
||||||
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
|
loadFile(extensionClasses, EXTENSIONS_INTERNAL_DIRECTORY);
|
||||||
cachedClasses.set(extensionClasses);
|
cachedClasses.set(extensionClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +249,6 @@ public class ExtensionLoader<T> {
|
|||||||
String fileName = dir + type.getName();
|
String fileName = dir + type.getName();
|
||||||
try {
|
try {
|
||||||
Enumeration<URL> urls;
|
Enumeration<URL> urls;
|
||||||
|
|
||||||
ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
|
ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
|
||||||
if (classLoader != null) {
|
if (classLoader != null) {
|
||||||
urls = classLoader.getResources(fileName);
|
urls = classLoader.getResources(fileName);
|
||||||
@ -296,7 +260,9 @@ public class ExtensionLoader<T> {
|
|||||||
while (urls.hasMoreElements()) {
|
while (urls.hasMoreElements()) {
|
||||||
URL url = urls.nextElement();
|
URL url = urls.nextElement();
|
||||||
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
|
try (
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))
|
||||||
|
) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
// delete comments
|
// delete comments
|
||||||
@ -311,7 +277,6 @@ public class ExtensionLoader<T> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
String name = null;
|
String name = null;
|
||||||
|
|
||||||
int i = line.indexOf('=');
|
int i = line.indexOf('=');
|
||||||
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
@ -327,47 +292,20 @@ public class ExtensionLoader<T> {
|
|||||||
+ clazz.getName() + "is not subtype of interface.");
|
+ clazz.getName() + "is not subtype of interface.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clazz.isAnnotationPresent(Adaptive.class)) {
|
|
||||||
if (adaptiveClass == null) {
|
|
||||||
adaptiveClass = clazz;
|
|
||||||
} else if (!adaptiveClass.equals(clazz)) {
|
|
||||||
throw new IllegalStateException("More than 1 adaptive class found: "
|
|
||||||
+ adaptiveClass.getName()
|
|
||||||
+ ", " + clazz.getName());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
clazz.getConstructor(type);
|
|
||||||
Set<Class<?>> wrappers = cachedWrapperClasses;
|
|
||||||
if (wrappers == null) {
|
|
||||||
cachedWrapperClasses = new HashSet<>();
|
|
||||||
wrappers = cachedWrapperClasses;
|
|
||||||
}
|
|
||||||
wrappers.add(clazz);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Constructor<? extends T> constructor = clazz.getConstructor();
|
|
||||||
if (name == null || name.length() == 0) {
|
if (name == null || name.length() == 0) {
|
||||||
|
// clazz: xxx.xxx.ZfyAPI
|
||||||
|
// type: xxx.xxx.API
|
||||||
|
// -> name: zfy
|
||||||
if (clazz.getSimpleName().length() > type.getSimpleName().length()
|
if (clazz.getSimpleName().length() > type.getSimpleName().length()
|
||||||
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
|
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
|
||||||
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
|
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length()
|
||||||
|
- type.getSimpleName().length()).toLowerCase();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
|
throw new IllegalStateException("No such extension name for the class "
|
||||||
|
+ clazz.getName() + " in the config " + url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Activate activate = clazz.getAnnotation(Activate.class);
|
|
||||||
|
|
||||||
if (activate != null) {
|
|
||||||
List<Object> activates = cachedActivates.get(type);
|
|
||||||
if (activates == null) {
|
|
||||||
synchronized (cachedActivates) {
|
|
||||||
activates = new ArrayList<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activates.add(constructor.newInstance());
|
|
||||||
cachedActivates.put(type, activates);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cachedNames.containsKey(clazz)) {
|
if (!cachedNames.containsKey(clazz)) {
|
||||||
cachedNames.put(clazz, name);
|
cachedNames.put(clazz, name);
|
||||||
@ -377,16 +315,17 @@ public class ExtensionLoader<T> {
|
|||||||
if (c == null) {
|
if (c == null) {
|
||||||
extensionClasses.put(name, clazz);
|
extensionClasses.put(name, clazz);
|
||||||
} else if (c != clazz) {
|
} else if (c != clazz) {
|
||||||
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName());
|
throw new IllegalStateException("Duplicate extension "
|
||||||
}
|
+ type.getName() + " name " + name + " on " + c.getName()
|
||||||
}
|
+ " and " + clazz.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), 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);
|
exceptions.put(line, e);
|
||||||
}
|
}
|
||||||
} // end of while read lines
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.error("Exception when load extension class(interface: " +
|
logger.error("Exception when load extension class(interface: " +
|
||||||
type + ", class file: " + url + ") in " + url, t);
|
type + ", class file: " + url + ") in " + url, t);
|
||||||
@ -406,9 +345,8 @@ public class ExtensionLoader<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds a value of type <code>T</code>.
|
* Holds a value of type <code>T</code>.
|
||||||
*
|
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static final class Holder<T> {
|
private static final class Holder<T> {
|
||||||
/**
|
/**
|
||||||
* The value contained in the holder.
|
* The value contained in the holder.
|
||||||
|
@ -28,11 +28,11 @@ import java.lang.annotation.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 把一个接口标识成扩展点。
|
* 把一个接口标识成扩展点。
|
||||||
* <p/>
|
* <p>
|
||||||
* 没有此注释的接口{@link ExtensionLoader}会拒绝接管。
|
* 没有此注释的接口 {@link ExtensionLoader} 会拒绝接管
|
||||||
*
|
*
|
||||||
|
* @author ehlxr
|
||||||
* @see ExtensionLoader
|
* @see ExtensionLoader
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@ -41,9 +41,6 @@ public @interface SPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* the default extension name.
|
* the default extension name.
|
||||||
*
|
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
*/
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* @author ehlxr
|
* @author ehlxr
|
||||||
* @since 2021-02-09 11:07.
|
* @since 2021-02-09 11:07.
|
||||||
*/
|
*/
|
||||||
public class ProtobufSerializer implements Serializer {
|
public class ProtostuffSerializer implements Serializer {
|
||||||
private static final Map<Class<?>, Schema<?>> CACHED_SCHEMA = new ConcurrentHashMap<>();
|
private static final Map<Class<?>, Schema<?>> CACHED_SCHEMA = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
@ -6,7 +6,7 @@ import io.github.ehlxr.did.extension.SPI;
|
|||||||
* @author ehlxr
|
* @author ehlxr
|
||||||
* @since 2021-02-08 22:12.
|
* @since 2021-02-08 22:12.
|
||||||
*/
|
*/
|
||||||
@SPI("jdk")
|
@SPI("protostuff")
|
||||||
public interface Serializer {
|
public interface Serializer {
|
||||||
/**
|
/**
|
||||||
* 将 obj 序列化成 byte 数组
|
* 将 obj 序列化成 byte 数组
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
package io.github.ehlxr.did.serializer;
|
package io.github.ehlxr.did.serializer;
|
||||||
|
|
||||||
|
import io.github.ehlxr.did.common.Constants;
|
||||||
import io.github.ehlxr.did.extension.ExtensionLoader;
|
import io.github.ehlxr.did.extension.ExtensionLoader;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ehlxr
|
* @author ehlxr
|
||||||
@ -8,6 +15,7 @@ import io.github.ehlxr.did.extension.ExtensionLoader;
|
|||||||
*/
|
*/
|
||||||
public final class SerializerHolder {
|
public final class SerializerHolder {
|
||||||
volatile private static Serializer serializer = null;
|
volatile private static Serializer serializer = null;
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SerializerHolder.class);
|
||||||
|
|
||||||
private SerializerHolder() {
|
private SerializerHolder() {
|
||||||
}
|
}
|
||||||
@ -16,7 +24,15 @@ public final class SerializerHolder {
|
|||||||
if (serializer == null) {
|
if (serializer == null) {
|
||||||
synchronized (SerializerHolder.class) {
|
synchronized (SerializerHolder.class) {
|
||||||
if (serializer == null) {
|
if (serializer == null) {
|
||||||
serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getDefaultExtension();
|
String serializerName = Constants.getEnv("DID_SERIALIZER");
|
||||||
|
if (!StringUtil.isNullOrEmpty(serializerName)) {
|
||||||
|
serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension(serializerName);
|
||||||
|
}
|
||||||
|
serializer = Objects.isNull(serializer) ?
|
||||||
|
ExtensionLoader.getExtensionLoader(Serializer.class).getDefaultExtension() :
|
||||||
|
serializer;
|
||||||
|
|
||||||
|
logger.debug("loaded {} serializer", serializer.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
jdk=io.github.ehlxr.did.serializer.JdkSerializer
|
#protostuff1=io.github.ehlxr.did.serializer.ProtostuffSerializer
|
||||||
protobuf=io.github.ehlxr.did.serializer.ProtobufSerializer
|
protostuff=io.github.ehlxr.did.serializer.ProtostuffSerializer
|
||||||
|
io.github.ehlxr.did.serializer.JdkSerializer
|
@ -1,10 +1,13 @@
|
|||||||
package io.github.ehlxr.did;
|
package io.github.ehlxr.did;
|
||||||
|
|
||||||
import io.github.ehlxr.did.client.SdkClient;
|
import io.github.ehlxr.did.client.SdkClient;
|
||||||
|
import io.github.ehlxr.did.common.Try;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,16 +37,16 @@ public class DidSdkTest {
|
|||||||
System.out.println("invokeync test finish");
|
System.out.println("invokeync test finish");
|
||||||
|
|
||||||
// 测试异步请求
|
// 测试异步请求
|
||||||
// final CountDownLatch countDownLatch = new CountDownLatch(NUM);
|
final CountDownLatch countDownLatch = new CountDownLatch(NUM);
|
||||||
// IntStream.range(0, NUM).parallel().forEach(i ->
|
IntStream.range(0, NUM).parallel().forEach(i ->
|
||||||
// Try.of(() -> client.invokeAsync(responseFuture -> {
|
Try.of(() -> client.invokeAsync(responseFuture -> {
|
||||||
// System.out.println(responseFuture.getSdkProto());
|
System.out.println(responseFuture.getSdkProto());
|
||||||
// countDownLatch.countDown();
|
countDownLatch.countDown();
|
||||||
// })).trap(Throwable::printStackTrace).run());
|
})).trap(Throwable::printStackTrace).run());
|
||||||
//
|
|
||||||
// //noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
// countDownLatch.await(10, TimeUnit.SECONDS);
|
countDownLatch.await(10, TimeUnit.SECONDS);
|
||||||
// System.out.println("invokeAsync test finish");
|
System.out.println("invokeAsync test finish");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.ehlxr.did;
|
package io.github.ehlxr.did;
|
||||||
|
|
||||||
import io.github.ehlxr.did.common.Constants;
|
import io.github.ehlxr.did.common.Constants;
|
||||||
|
import io.github.ehlxr.did.generator.SnowFlake;
|
||||||
import io.github.ehlxr.did.server.Server;
|
import io.github.ehlxr.did.server.Server;
|
||||||
import io.github.ehlxr.did.server.http.HttpServer;
|
import io.github.ehlxr.did.server.http.HttpServer;
|
||||||
import io.github.ehlxr.did.server.sdk.SdkServer;
|
import io.github.ehlxr.did.server.sdk.SdkServer;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package io.github.ehlxr.did;
|
package io.github.ehlxr.did.generator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* twitter 的 snowflake 算法 -- java 实现
|
* twitter 的 snowflake 算法 -- java 实现
|
@ -1,6 +1,6 @@
|
|||||||
package io.github.ehlxr.did.server;
|
package io.github.ehlxr.did.server;
|
||||||
|
|
||||||
import io.github.ehlxr.did.SnowFlake;
|
import io.github.ehlxr.did.generator.SnowFlake;
|
||||||
import io.github.ehlxr.did.common.Try;
|
import io.github.ehlxr.did.common.Try;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package io.github.ehlxr.did.server.http;
|
package io.github.ehlxr.did.server.http;
|
||||||
|
|
||||||
import io.github.ehlxr.did.SnowFlake;
|
import io.github.ehlxr.did.generator.SnowFlake;
|
||||||
import io.github.ehlxr.did.common.Constants;
|
import io.github.ehlxr.did.common.Constants;
|
||||||
import io.github.ehlxr.did.common.Try;
|
import io.github.ehlxr.did.common.Try;
|
||||||
import io.github.ehlxr.did.server.BaseServer;
|
import io.github.ehlxr.did.server.BaseServer;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package io.github.ehlxr.did.server.http;
|
package io.github.ehlxr.did.server.http;
|
||||||
|
|
||||||
import io.github.ehlxr.did.SdkProto;
|
import io.github.ehlxr.did.SdkProto;
|
||||||
import io.github.ehlxr.did.SnowFlake;
|
import io.github.ehlxr.did.generator.SnowFlake;
|
||||||
import io.github.ehlxr.did.common.Constants;
|
import io.github.ehlxr.did.common.Constants;
|
||||||
import io.github.ehlxr.did.common.NettyUtil;
|
import io.github.ehlxr.did.common.NettyUtil;
|
||||||
import io.github.ehlxr.did.common.Result;
|
import io.github.ehlxr.did.common.Result;
|
||||||
@ -66,14 +66,14 @@ public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpReque
|
|||||||
response.setStatus(status)
|
response.setStatus(status)
|
||||||
.content().writeBytes(result.toString().getBytes());
|
.content().writeBytes(result.toString().getBytes());
|
||||||
|
|
||||||
logger.debug("http server handler write response {} restul {} to channel", status, result);
|
logger.debug("http server handler write response {} result {} to channel", status, result);
|
||||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
Channel channel = ctx.channel();
|
Channel channel = ctx.channel();
|
||||||
logger.error("HttpServerHandler channel [{}] error and will be closed", NettyUtil.parseRemoteAddr(channel), cause);
|
logger.error("channel {} will be closed, 'cause of ", NettyUtil.parseRemoteAddr(channel), cause);
|
||||||
NettyUtil.closeChannel(channel);
|
NettyUtil.closeChannel(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package io.github.ehlxr.did.server.sdk;
|
package io.github.ehlxr.did.server.sdk;
|
||||||
|
|
||||||
import io.github.ehlxr.did.SnowFlake;
|
import io.github.ehlxr.did.generator.SnowFlake;
|
||||||
import io.github.ehlxr.did.adapter.MessageDecoder;
|
import io.github.ehlxr.did.adapter.MessageDecoder;
|
||||||
import io.github.ehlxr.did.adapter.MessageEncoder;
|
import io.github.ehlxr.did.adapter.MessageEncoder;
|
||||||
import io.github.ehlxr.did.common.Constants;
|
import io.github.ehlxr.did.common.Constants;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package io.github.ehlxr.did.server.sdk;
|
package io.github.ehlxr.did.server.sdk;
|
||||||
|
|
||||||
import io.github.ehlxr.did.SdkProto;
|
import io.github.ehlxr.did.SdkProto;
|
||||||
import io.github.ehlxr.did.SnowFlake;
|
|
||||||
import io.github.ehlxr.did.adapter.Message;
|
import io.github.ehlxr.did.adapter.Message;
|
||||||
import io.github.ehlxr.did.common.Constants;
|
import io.github.ehlxr.did.common.Constants;
|
||||||
import io.github.ehlxr.did.common.NettyUtil;
|
import io.github.ehlxr.did.common.NettyUtil;
|
||||||
|
import io.github.ehlxr.did.generator.SnowFlake;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
@ -56,7 +56,7 @@ public class SdkServerHandler extends SimpleChannelInboundHandler<Message<SdkPro
|
|||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
Channel channel = ctx.channel();
|
Channel channel = ctx.channel();
|
||||||
logger.error("SdkServerHandler channel [{}] error and will be closed", NettyUtil.parseRemoteAddr(channel), cause);
|
logger.error("channel {} will be closed, 'cause of ", NettyUtil.parseRemoteAddr(channel), cause);
|
||||||
NettyUtil.closeChannel(channel);
|
NettyUtil.closeChannel(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user