Skip to content

Commit

Permalink
Enable dynamically load the extension from code. (sofastack#912)
Browse files Browse the repository at this point in the history
  • Loading branch information
OrezzerO authored May 15, 2020
1 parent b936f9f commit cbe3c4c
Show file tree
Hide file tree
Showing 19 changed files with 192 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public class RouterChain {
private final static ExtensionLoader<Router> EXTENSION_LOADER = buildLoader();

private static ExtensionLoader<Router> buildLoader() {
return ExtensionLoaderFactory.getExtensionLoader(Router.class, new ExtensionLoaderListener<Router>() {
ExtensionLoader<Router> extensionLoader = ExtensionLoaderFactory.getExtensionLoader(Router.class);
extensionLoader.addListener(new ExtensionLoaderListener<Router>() {
@Override
public void onLoad(ExtensionClass<Router> extensionClass) {
Class<? extends Router> implClass = extensionClass.getClazz();
Expand All @@ -90,6 +91,7 @@ public void onLoad(ExtensionClass<Router> extensionClass) {
}
}
});
return extensionLoader;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,16 @@ public final class CompressorFactory {
private final static ExtensionLoader<Compressor> EXTENSION_LOADER = buildLoader();

private static ExtensionLoader<Compressor> buildLoader() {
return ExtensionLoaderFactory.getExtensionLoader(Compressor.class, new ExtensionLoaderListener<Compressor>() {
ExtensionLoader<Compressor> extensionLoader = ExtensionLoaderFactory.getExtensionLoader(Compressor.class);
extensionLoader.addListener(new ExtensionLoaderListener<Compressor>() {
@Override
public void onLoad(ExtensionClass<Compressor> extensionClass) {
// 除了保留 tag:Compressor外, 需要保留 code:Compressor
TYPE_COMPRESSOR_MAP.put(extensionClass.getCode(), extensionClass.getExtInstance());
TYPE_CODE_MAP.put(extensionClass.getAlias(), extensionClass.getCode());
}
});
return extensionLoader;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ public final class SerializerFactory {
private final static ExtensionLoader<Serializer> EXTENSION_LOADER = buildLoader();

private static ExtensionLoader<Serializer> buildLoader() {
return ExtensionLoaderFactory.getExtensionLoader(Serializer.class,
new ExtensionLoaderListener<Serializer>() {
@Override
public void onLoad(ExtensionClass<Serializer> extensionClass) {
// 除了保留 tag:Serializer外, 需要保留 code:Serializer
TYPE_SERIALIZER_MAP.put(extensionClass.getCode(), extensionClass.getExtInstance());
TYPE_CODE_MAP.put(extensionClass.getAlias(), extensionClass.getCode());
}
});
ExtensionLoader<Serializer> extensionLoader = ExtensionLoaderFactory.getExtensionLoader(Serializer.class);
extensionLoader.addListener(new ExtensionLoaderListener<Serializer>() {
@Override
public void onLoad(ExtensionClass<Serializer> extensionClass) {
// 除了保留 tag:Serializer外, 需要保留 code:Serializer
TYPE_SERIALIZER_MAP.put(extensionClass.getCode(), extensionClass.getExtInstance());
TYPE_CODE_MAP.put(extensionClass.getAlias(), extensionClass.getCode());
}
});
return extensionLoader;

}

/**
Expand Down
90 changes: 64 additions & 26 deletions core/api/src/main/java/com/alipay/sofa/rpc/ext/ExtensionLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.io.InputStreamReader;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
Expand All @@ -49,8 +50,9 @@ public class ExtensionLoader<T> {
/**
* slf4j Logger for this class
*/
private final static Logger LOGGER = LoggerFactory
.getLogger(ExtensionLoader.class);
private final static Logger LOGGER = LoggerFactory
.getLogger(ExtensionLoader.class);
private static final String LOAD_FROM_CODE = "DYNAMIC LOAD EXTENSION BY CODE";

/**
* 当前加载的接口类名
Expand Down Expand Up @@ -80,7 +82,7 @@ public class ExtensionLoader<T> {
/**
* 加载监听器
*/
protected final ExtensionLoaderListener<T> listener;
protected final List<ExtensionLoaderListener<T>> listeners;

/**
* 构造函数(自动加载)
Expand Down Expand Up @@ -112,24 +114,27 @@ protected ExtensionLoader(Class<T> interfaceClass, boolean autoLoad, ExtensionLo
if (RpcRunningState.isShuttingDown()) {
this.interfaceClass = null;
this.interfaceName = null;
this.listener = null;
this.listeners = null;
this.factory = null;
this.extensible = null;
this.all = null;
return;
}
// 接口为空,既不是接口,也不是抽象类
if (interfaceClass == null ||
!(interfaceClass.isInterface() || Modifier.isAbstract(interfaceClass.getModifiers()))) {
!(interfaceClass.isInterface() || Modifier.isAbstract(interfaceClass.getModifiers()))) {
throw new IllegalArgumentException("Extensible class must be interface or abstract class!");
}
this.interfaceClass = interfaceClass;
this.interfaceName = ClassTypeUtils.getTypeStr(interfaceClass);
this.listener = listener;
this.listeners = new ArrayList<>();
if (listener != null) {
listeners.add(listener);
}
Extensible extensible = interfaceClass.getAnnotation(Extensible.class);
if (extensible == null) {
throw new IllegalArgumentException(
"Error when load extensible interface " + interfaceName + ", must add annotation @Extensible.");
"Error when load extensible interface " + interfaceName + ", must add annotation @Extensible.");
} else {
this.extensible = extensible;
}
Expand Down Expand Up @@ -219,23 +224,28 @@ protected void readLine(URL url, String line) {
}
return;
}
if (!interfaceClass.isAssignableFrom(tmp)) {

loadExtension(alias, tmp, StringUtils.toString(url), className);
}

private void loadExtension(String alias, Class loadedClazz, String location, String className) {
if (!interfaceClass.isAssignableFrom(loadedClazz)) {
throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
" from file:" + url + ", " + className + " is not subtype of interface.");
" from file:" + location + ", " + className + " is not subtype of interface.");
}
Class<? extends T> implClass = (Class<? extends T>) tmp;
Class<? extends T> implClass = (Class<? extends T>) loadedClazz;

// 检查是否有可扩展标识
Extension extension = implClass.getAnnotation(Extension.class);
if (extension == null) {
throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
" from file:" + url + ", " + className + " must add annotation @Extension.");
" from file:" + location + ", " + className + " must add annotation @Extension.");
} else {
String aliasInCode = extension.value();
if (StringUtils.isBlank(aliasInCode)) {
// 扩展实现类未配置@Extension 标签
throw new IllegalArgumentException("Error when load extension of extensible " + interfaceClass +
" from file:" + url + ", " + className + "'s alias of @Extension is blank");
" from file:" + location + ", " + className + "'s alias of @Extension is blank");
}
if (alias == null) {
// spi文件里没配置,用代码里的
Expand All @@ -244,20 +254,20 @@ protected void readLine(URL url, String line) {
// spi文件里配置的和代码里的不一致
if (!aliasInCode.equals(alias)) {
throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
" from file:" + url + ", aliases of " + className + " are " +
" from file:" + location + ", aliases of " + className + " are " +
"not equal between " + aliasInCode + "(code) and " + alias + "(file).");
}
}
// 接口需要编号,实现类没设置
if (extensible.coded() && extension.code() < 0) {
throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
" from file:" + url + ", code of @Extension must >=0 at " + className + ".");
" from file:" + location + ", code of @Extension must >=0 at " + className + ".");
}
}
// 不可以是default和*
if (StringUtils.DEFAULT.equals(alias) || StringUtils.ALL.equals(alias)) {
throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
" from file:" + url + ", alias of @Extension must not \"default\" and \"*\" at " + className + ".");
" from file:" + location + ", alias of @Extension must not \"default\" and \"*\" at " + className + ".");
}
// 检查是否有存在同名的
ExtensionClass old = all.get(alias);
Expand Down Expand Up @@ -292,7 +302,7 @@ protected void readLine(URL url, String line) {
} else {
// 如果不能被覆盖,抛出已存在异常
throw new IllegalStateException(
"Error when load extension of extensible " + interfaceClass + " from file:" + url +
"Error when load extension of extensible " + interfaceClass + " from file:" + location +
", Duplicate class with same alias: " + alias + ", " + old.getClazz() + " and " + implClass);
}
}
Expand Down Expand Up @@ -355,19 +365,20 @@ private ExtensionClass<T> buildClass(Extension extension, Class<? extends T> imp
}

private void loadSuccess(String alias, ExtensionClass<T> extensionClass) {
if (listener != null) {
try {
listener.onLoad(extensionClass); // 加载完毕,通知监听器
all.put(alias, extensionClass);
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Error when load extension of extensible " + interfaceClass + " with alias: "
+ alias + ".", e);
if (listeners != null) {
for (ExtensionLoaderListener<T> listener : listeners) {
try {
listener.onLoad(extensionClass);
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Error when load extension of extensible " + interfaceClass + " with alias: "
+ alias + ".", e);
}
}
}
} else {
all.put(alias, extensionClass);

}
all.put(alias, extensionClass);
}

protected String[] parseAliasAndClassName(String line) {
Expand Down Expand Up @@ -476,4 +487,31 @@ public T getExtension(String alias, Class[] argTypes, Object[] args) {
}
}
}

public void loadExtension(Class loadedClass) {
if (loadedClass == null) {
throw new IllegalArgumentException("Can not load extension of null");
}
loadExtension(null, loadedClass, LOAD_FROM_CODE, loadedClass.getName());
}

public void addListener(ExtensionLoaderListener<T> listener) {
synchronized (this) {
if (!listeners.contains(listener)) {
this.listeners.add(listener);
for (ExtensionClass<T> value : all.values()) {
try {
listener.onLoad(value);
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Error when notify listener of extensible " + interfaceClass +
" with alias: "
+ value.getAlias() + ".", e);
}
}
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ private ExtensionLoaderFactory() {
/**
* Get extension loader by extensible class with listener
*
* This method is deprecated, use com.alipay.sofa.rpc.ext.ExtensionLoaderFactory#getExtensionLoader(java.lang.Class) instead.
* Use com.alipay.sofa.rpc.ext.ExtensionLoader#addListener(com.alipay.sofa.rpc.ext.ExtensionLoaderListener) to add listener.
*
* @deprecated
* @param clazz Extensible class
* @param listener Listener of ExtensionLoader
* @param <T> Class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public class FilterChain implements Invoker {
private final static ExtensionLoader<Filter> EXTENSION_LOADER = buildLoader();

private static ExtensionLoader<Filter> buildLoader() {
return ExtensionLoaderFactory.getExtensionLoader(Filter.class, new ExtensionLoaderListener<Filter>() {
ExtensionLoader<Filter> extensionLoader = ExtensionLoaderFactory.getExtensionLoader(Filter.class);
extensionLoader.addListener(new ExtensionLoaderListener<Filter>() {
@Override
public void onLoad(ExtensionClass<Filter> extensionClass) {
Class<? extends Filter> implClass = extensionClass.getClazz();
Expand All @@ -95,6 +96,7 @@ public void onLoad(ExtensionClass<Filter> extensionClass) {
}
}
});
return extensionLoader;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public class ProtocolFactory {
private final static ExtensionLoader<Protocol> EXTENSION_LOADER = buildLoader();

private static ExtensionLoader<Protocol> buildLoader() {
return ExtensionLoaderFactory.getExtensionLoader(Protocol.class, new ExtensionLoaderListener<Protocol>() {
ExtensionLoader<Protocol> extensionLoader = ExtensionLoaderFactory.getExtensionLoader(Protocol.class);
extensionLoader.addListener(new ExtensionLoaderListener<Protocol>() {
@Override
public void onLoad(ExtensionClass<Protocol> extensionClass) {
// 除了保留 alias:Protocol外, 需要保留 code:Protocol
Expand All @@ -67,6 +68,7 @@ public void onLoad(ExtensionClass<Protocol> extensionClass) {
}
}
});
return extensionLoader;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

/**
* Factory of TelnetHandler
*
*
* @author <a href=mailto:[email protected]>GengZhang</a>
*/
@Unstable
Expand All @@ -52,18 +52,19 @@ public class TelnetHandlerFactory {
private final static ExtensionLoader<TelnetHandler> EXTENSION_LOADER = buildLoader();

private static ExtensionLoader<TelnetHandler> buildLoader() {
return ExtensionLoaderFactory.getExtensionLoader(TelnetHandler.class,
new ExtensionLoaderListener<TelnetHandler>() {
@Override
public void onLoad(ExtensionClass<TelnetHandler> extensionClass) {
// 自己维护支持列表,不托管给ExtensionLoaderFactory
TelnetHandler handler = extensionClass.getExtInstance();
supportedCmds.put(handler.getCommand(), handler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Add telnet handler {}:{}.", handler.getCommand(), handler);
}
ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoaderFactory.getExtensionLoader(TelnetHandler.class);
extensionLoader.addListener(new ExtensionLoaderListener<TelnetHandler>() {
@Override
public void onLoad(ExtensionClass<TelnetHandler> extensionClass) {
// 自己维护支持列表,不托管给ExtensionLoaderFactory
TelnetHandler handler = extensionClass.getExtInstance();
supportedCmds.put(handler.getCommand(), handler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Add telnet handler {}:{}.", handler.getCommand(), handler);
}
});
}
});
return extensionLoader;
}

public static TelnetHandler getHandler(String command) {
Expand Down
35 changes: 35 additions & 0 deletions core/api/src/test/java/com/alipay/sofa/rpc/ext/DynamicFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.rpc.ext;

import com.alipay.sofa.rpc.core.exception.SofaRpcException;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.Filter;
import com.alipay.sofa.rpc.filter.FilterInvoker;

/**
* @author zhaowang
* @version : DynamicFilter.java, v 0.1 2020年05月08日 1:59 下午 zhaowang Exp $
*/
@Extension("dynamic0")
public class DynamicFilter extends Filter {
@Override
public SofaResponse invoke(FilterInvoker invoker, SofaRequest request) throws SofaRpcException {
return null;
}
}
Loading

0 comments on commit cbe3c4c

Please sign in to comment.