Refactor SpiLoader and enhance SPI mechanism (#1383)
* Add `@Spi` annotation as the general annotation for SPI definition. * Add isDefault in @Spi, add loadDefaultInstance and improve loadFirstInstanceOrDefault method, improve test cases * Add SpiLoaderException class for thrown when something goes wrong while loading Provider * Rearrange packages of base SPI mechanism NOTE: this PR contains breaking changes regarding API.
This commit is contained in:
parent
a343873c81
commit
62efb78d4e
|
|
@ -27,7 +27,7 @@ import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
|||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +59,7 @@ public final class GatewayApiDefinitionManager {
|
|||
}
|
||||
|
||||
private static void initializeApiChangeObserverSpi() {
|
||||
List<ApiDefinitionChangeObserver> listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class);
|
||||
List<ApiDefinitionChangeObserver> listeners = SpiLoader.of(ApiDefinitionChangeObserver.class).loadInstanceList();
|
||||
for (ApiDefinitionChangeObserver e : listeners) {
|
||||
API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {}"
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker;
|
|||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.1
|
||||
*/
|
||||
@SpiOrder(-4000)
|
||||
@Spi(order = -4000)
|
||||
public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
|
|||
* @author Eric Zhao
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @deprecated since 1.7.2, we can use @SpiOrder(-4000) to adjust the order of {@link GatewayFlowSlot},
|
||||
* @deprecated since 1.7.2, we can use @Spi(order = -4000) to adjust the order of {@link GatewayFlowSlot},
|
||||
* this class is reserved for compatibility with older versions.
|
||||
*
|
||||
* @see GatewayFlowSlot
|
||||
* @see DefaultSlotChainBuilder
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.cluster.client.codec;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter;
|
||||
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
|
|
@ -34,7 +34,7 @@ public final class ClientEntityCodecProvider {
|
|||
}
|
||||
|
||||
private static void resolveInstance() {
|
||||
RequestEntityWriter writer = SpiLoader.loadFirstInstance(RequestEntityWriter.class);
|
||||
RequestEntityWriter writer = SpiLoader.of(RequestEntityWriter.class).loadFirstInstance();
|
||||
if (writer == null) {
|
||||
RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed");
|
||||
} else {
|
||||
|
|
@ -42,7 +42,7 @@ public final class ClientEntityCodecProvider {
|
|||
RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}",
|
||||
requestEntityWriter.getClass().getCanonicalName());
|
||||
}
|
||||
ResponseEntityDecoder decoder = SpiLoader.loadFirstInstance(ResponseEntityDecoder.class);
|
||||
ResponseEntityDecoder decoder = SpiLoader.of(ResponseEntityDecoder.class).loadFirstInstance();
|
||||
if (decoder == null) {
|
||||
RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
|
|||
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterParamFlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ import java.util.Collection;
|
|||
* @author Eric Zhao
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Spi(isDefault = true)
|
||||
public class DefaultTokenService implements TokenService {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
package com.alibaba.csp.sentinel.cluster.server;
|
||||
|
||||
import com.alibaba.csp.sentinel.cluster.TokenService;
|
||||
import com.alibaba.csp.sentinel.cluster.flow.DefaultTokenService;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -37,7 +36,7 @@ public final class TokenServiceProvider {
|
|||
}
|
||||
|
||||
private static void resolveTokenServiceSpi() {
|
||||
service = SpiLoader.loadFirstInstanceOrDefault(TokenService.class, DefaultTokenService.class);
|
||||
service = SpiLoader.of(TokenService.class).loadFirstInstanceOrDefault();
|
||||
if (service != null) {
|
||||
RecordLog.info("[TokenServiceProvider] Global token service resolved: "
|
||||
+ service.getClass().getCanonicalName());
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.cluster.server.codec;
|
|||
import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityDecoder;
|
||||
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityWriter;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -34,7 +34,7 @@ public final class ServerEntityCodecProvider {
|
|||
}
|
||||
|
||||
private static void resolveInstance() {
|
||||
ResponseEntityWriter writer = SpiLoader.loadFirstInstance(ResponseEntityWriter.class);
|
||||
ResponseEntityWriter writer = SpiLoader.of(ResponseEntityWriter.class).loadFirstInstance();
|
||||
if (writer == null) {
|
||||
RecordLog.warn("[ServerEntityCodecProvider] No existing response entity writer, resolve failed");
|
||||
} else {
|
||||
|
|
@ -42,7 +42,7 @@ public final class ServerEntityCodecProvider {
|
|||
RecordLog.info("[ServerEntityCodecProvider] Response entity writer resolved: {}",
|
||||
responseEntityWriter.getClass().getCanonicalName());
|
||||
}
|
||||
RequestEntityDecoder decoder = SpiLoader.loadFirstInstance(RequestEntityDecoder.class);
|
||||
RequestEntityDecoder decoder = SpiLoader.of(RequestEntityDecoder.class).loadFirstInstance();
|
||||
if (decoder == null) {
|
||||
RecordLog.warn("[ServerEntityCodecProvider] No existing request entity decoder, resolve failed");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.cluster.server.processor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.csp.sentinel.cluster.annotation.RequestType;
|
||||
import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
|
|
@ -31,15 +31,13 @@ public final class RequestProcessorProvider {
|
|||
|
||||
private static final Map<Integer, RequestProcessor> PROCESSOR_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ServiceLoader<RequestProcessor> SERVICE_LOADER = ServiceLoaderUtil.getServiceLoader(
|
||||
RequestProcessor.class);
|
||||
|
||||
static {
|
||||
loadAndInit();
|
||||
}
|
||||
|
||||
private static void loadAndInit() {
|
||||
for (RequestProcessor processor : SERVICE_LOADER) {
|
||||
List<RequestProcessor> processors = SpiLoader.of(RequestProcessor.class).loadInstanceList();
|
||||
for (RequestProcessor processor : processors) {
|
||||
Integer type = parseRequestType(processor);
|
||||
if (type != null) {
|
||||
PROCESSOR_MAP.put(type, processor);
|
||||
|
|
|
|||
|
|
@ -70,5 +70,17 @@ public final class Constants {
|
|||
*/
|
||||
public static volatile boolean ON = true;
|
||||
|
||||
/**
|
||||
* Order of default processor slots
|
||||
*/
|
||||
public static final int ORDER_NODE_SELECTOR_SLOT = -10000;
|
||||
public static final int ORDER_CLUSTER_BUILDER_SLOT = -9000;
|
||||
public static final int ORDER_LOG_SLOT = -8000;
|
||||
public static final int ORDER_STATISTIC_SLOT = -7000;
|
||||
public static final int ORDER_AUTHORITY_SLOT = -6000;
|
||||
public static final int ORDER_SYSTEM_SLOT = -5000;
|
||||
public static final int ORDER_FLOW_SLOT = -2000;
|
||||
public static final int ORDER_DEGRADE_SLOT = -1000;
|
||||
|
||||
private Constants() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
package com.alibaba.csp.sentinel.cluster.client;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* Provider for a universal {@link ClusterTokenClient} instance.
|
||||
|
|
@ -38,7 +38,7 @@ public final class TokenClientProvider {
|
|||
}
|
||||
|
||||
private static void resolveTokenClientInstance() {
|
||||
ClusterTokenClient resolvedClient = SpiLoader.loadFirstInstance(ClusterTokenClient.class);
|
||||
ClusterTokenClient resolvedClient = SpiLoader.of(ClusterTokenClient.class).loadFirstInstance();
|
||||
if (resolvedClient == null) {
|
||||
RecordLog.info(
|
||||
"[TokenClientProvider] No existing cluster token client, cluster client mode will not be activated");
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
package com.alibaba.csp.sentinel.cluster.server;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -31,7 +31,7 @@ public final class EmbeddedClusterTokenServerProvider {
|
|||
}
|
||||
|
||||
private static void resolveInstance() {
|
||||
EmbeddedClusterTokenServer s = SpiLoader.loadFirstInstance(EmbeddedClusterTokenServer.class);
|
||||
EmbeddedClusterTokenServer s = SpiLoader.of(EmbeddedClusterTokenServer.class).loadFirstInstance();
|
||||
if (s == null) {
|
||||
RecordLog.warn("[EmbeddedClusterTokenServerProvider] No existing cluster token server, cluster server mode will not be activated");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ public final class SentinelConfig {
|
|||
*/
|
||||
public static final int APP_TYPE_COMMON = 0;
|
||||
|
||||
/**
|
||||
* Parameter value for using context classloader.
|
||||
*/
|
||||
private static final String CLASSLOADER_CONTEXT = "context";
|
||||
|
||||
private static final Map<String, String> props = new ConcurrentHashMap<>();
|
||||
|
||||
private static int appType = APP_TYPE_COMMON;
|
||||
|
|
@ -307,6 +312,15 @@ public final class SentinelConfig {
|
|||
private static String toEnvKey(/*@NotBlank*/ String propKey) {
|
||||
return propKey.toUpperCase().replace('.', '_');
|
||||
}
|
||||
/**
|
||||
* Whether use context classloader via config parameter
|
||||
*
|
||||
* @return Whether use context classloader
|
||||
*/
|
||||
public static boolean shouldUseContextClassloader() {
|
||||
String classloaderConf = SentinelConfig.getConfig(SentinelConfig.SPI_CLASSLOADER);
|
||||
return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf);
|
||||
}
|
||||
|
||||
private SentinelConfig() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import java.util.ServiceLoader;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* Load registered init functions and execute in order.
|
||||
|
|
@ -43,9 +43,9 @@ public final class InitExecutor {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class);
|
||||
List<InitFunc> initFuncs = SpiLoader.of(InitFunc.class).loadInstanceListSorted();
|
||||
List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
|
||||
for (InitFunc initFunc : loader) {
|
||||
for (InitFunc initFunc : initFuncs) {
|
||||
RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName());
|
||||
insertSorted(initList, initFunc);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* Get all {@link MetricExtension} via SPI.
|
||||
|
|
@ -35,7 +35,7 @@ public class MetricExtensionProvider {
|
|||
}
|
||||
|
||||
private static void resolveInstance() {
|
||||
List<MetricExtension> extensions = SpiLoader.loadInstanceList(MetricExtension.class);
|
||||
List<MetricExtension> extensions = SpiLoader.of(MetricExtension.class).loadInstanceList();
|
||||
|
||||
if (extensions.isEmpty()) {
|
||||
RecordLog.info("[MetricExtensionProvider] No existing MetricExtension found");
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.slotchain;
|
|||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* A provider for creating slot chains via resolved slot chain builder SPI.
|
||||
|
|
@ -41,7 +41,7 @@ public final class SlotChainProvider {
|
|||
}
|
||||
|
||||
// Resolve the slot chain builder SPI.
|
||||
slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class);
|
||||
slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
|
||||
|
||||
if (slotChainBuilder == null) {
|
||||
// Should not go through here.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
|
|||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
|
||||
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -31,14 +32,14 @@ import java.util.List;
|
|||
* @author qinan.qn
|
||||
* @author leyou
|
||||
*/
|
||||
@Spi(isDefault = true)
|
||||
public class DefaultSlotChainBuilder implements SlotChainBuilder {
|
||||
|
||||
@Override
|
||||
public ProcessorSlotChain build() {
|
||||
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
|
||||
|
||||
// Note: the instances of ProcessorSlot should be different, since they are not stateless.
|
||||
List<ProcessorSlot> sortedSlotList = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class);
|
||||
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
|
||||
for (ProcessorSlot slot : sortedSlotList) {
|
||||
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
|
||||
RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
|
||||
|
|
|
|||
|
|
@ -18,12 +18,13 @@ package com.alibaba.csp.sentinel.slots.block.authority;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* A {@link ProcessorSlot} that dedicates to {@link AuthorityRule} checking.
|
||||
|
|
@ -31,7 +32,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder;
|
|||
* @author leyou
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SpiOrder(-6000)
|
||||
@Spi(order = Constants.ORDER_AUTHORITY_SLOT)
|
||||
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package com.alibaba.csp.sentinel.slots.block.degrade;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
|
|
@ -25,7 +26,7 @@ import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
|
|||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* A {@link ProcessorSlot} dedicates to circuit breaking.
|
||||
|
|
@ -33,7 +34,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder;
|
|||
* @author Carpenter Lee
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SpiOrder(-1000)
|
||||
@Spi(order = Constants.ORDER_DEGRADE_SLOT)
|
||||
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.slots.block.flow;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.function.Function;
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ import java.util.Map;
|
|||
* @author jialiang.linjl
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SpiOrder(-2000)
|
||||
@Spi(order = Constants.ORDER_FLOW_SLOT)
|
||||
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
private final FlowRuleChecker checker;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.slots.clusterbuilder;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
|
|
@ -30,7 +31,7 @@ import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
|||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
|
@ -45,7 +46,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder;
|
|||
*
|
||||
* @author jialiang.linjl
|
||||
*/
|
||||
@SpiOrder(-9000)
|
||||
@Spi(isSingleton = false, order = Constants.ORDER_CLUSTER_BUILDER_SLOT)
|
||||
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,19 +15,20 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.slots.logger;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* A {@link com.alibaba.csp.sentinel.slotchain.ProcessorSlot} that is response for logging block exceptions
|
||||
* to provide concrete logs for troubleshooting.
|
||||
*/
|
||||
@SpiOrder(-8000)
|
||||
@Spi(order = Constants.ORDER_LOG_SLOT)
|
||||
public class LogSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.slots.nodeselector;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
|
|
@ -22,7 +23,7 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
|
|||
import com.alibaba.csp.sentinel.node.EntranceNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
|
@ -123,7 +124,7 @@ import java.util.Map;
|
|||
* @see EntranceNode
|
||||
* @see ContextUtil
|
||||
*/
|
||||
@SpiOrder(-10000)
|
||||
@Spi(isSingleton = false, order = Constants.ORDER_NODE_SELECTOR_SLOT)
|
||||
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import com.alibaba.csp.sentinel.node.Node;
|
|||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback;
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.PriorityWaitException;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
import com.alibaba.csp.sentinel.util.TimeUtil;
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
|
|
@ -48,7 +48,7 @@ import com.alibaba.csp.sentinel.slots.block.BlockException;
|
|||
* @author jialiang.linjl
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SpiOrder(-7000)
|
||||
@Spi(order = Constants.ORDER_STATISTIC_SLOT)
|
||||
public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.slots.system;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* A {@link ProcessorSlot} that dedicates to {@link SystemRule} checking.
|
||||
|
|
@ -28,7 +29,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder;
|
|||
* @author jialiang.linjl
|
||||
* @author leyou
|
||||
*/
|
||||
@SpiOrder(-5000)
|
||||
@Spi(order = Constants.ORDER_SYSTEM_SLOT)
|
||||
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.spi;
|
||||
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public final class ServiceLoaderUtil {
|
||||
|
||||
private static final String CLASSLOADER_DEFAULT = "default";
|
||||
private static final String CLASSLOADER_CONTEXT = "context";
|
||||
|
||||
public static <S> ServiceLoader<S> getServiceLoader(Class<S> clazz) {
|
||||
if (shouldUseContextClassloader()) {
|
||||
return ServiceLoader.load(clazz);
|
||||
} else {
|
||||
return ServiceLoader.load(clazz, clazz.getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldUseContextClassloader() {
|
||||
String classloaderConf = SentinelConfig.getConfig(SentinelConfig.SPI_CLASSLOADER);
|
||||
return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf);
|
||||
}
|
||||
|
||||
private ServiceLoaderUtil() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.spi;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation for Provider class of SPI.
|
||||
*
|
||||
* @see SpiLoader
|
||||
* @author cdfive
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Documented
|
||||
public @interface Spi {
|
||||
|
||||
/**
|
||||
* Alias name of Provider class
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Whether create singleton instance
|
||||
*/
|
||||
boolean isSingleton() default true;
|
||||
|
||||
/**
|
||||
* Whether is the default Provider
|
||||
*/
|
||||
boolean isDefault() default false;
|
||||
|
||||
/**
|
||||
* Order priority of Provider class
|
||||
*/
|
||||
int order() default 0;
|
||||
|
||||
int ORDER_HIGHEST = Integer.MIN_VALUE;
|
||||
|
||||
int ORDER_LOWEST = Integer.MAX_VALUE;
|
||||
}
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.spi;
|
||||
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A simple SPI loading facility.
|
||||
*
|
||||
* <p>SPI is short for Service Provider Interface.</p>
|
||||
*
|
||||
* <p>
|
||||
* Service is represented by a single type, that is, a single interface or an abstract class.
|
||||
* Provider is implementations of Service, that is, some classes which implement the interface or extends the abstract class.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For Service type:
|
||||
* Must interface or abstract class.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For Provider class:
|
||||
* Must have a zero-argument constructor so that they can be instantiated during loading.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For Provider configuration file:
|
||||
* 1. The file contains a list of fully-qualified binary names of concrete provider classes, one per line.
|
||||
* 2. Space and tab characters surrounding each name, as well as blank lines, are ignored.
|
||||
* 3. The comment line character is #, all characters following it are ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Provide common functions, such as:
|
||||
* Load all Provider instance unsorted/sorted list.
|
||||
* Load highest/lowest order priority instance.
|
||||
* Load first-found or default instance.
|
||||
* Load instance by aliasname or provider class.
|
||||
* </p>
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @author cdfive
|
||||
* @since 1.4.0
|
||||
* @see com.alibaba.csp.sentinel.spi.Spi
|
||||
* @see java.util.ServiceLoader
|
||||
*/
|
||||
public final class SpiLoader<S> {
|
||||
|
||||
// Default path for the folder of Provider configuration file
|
||||
private static final String SPI_FILE_PREFIX = "META-INF/services/";
|
||||
|
||||
// Cache the SpiLoader instances, key: classname of Service, value: SpiLoader instance
|
||||
private static final ConcurrentHashMap<String, SpiLoader> SPI_LOADER_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
// Cache the classes of Provider
|
||||
private final List<Class<? extends S>> classList = Collections.synchronizedList(new ArrayList<Class<? extends S>>());
|
||||
|
||||
// Cache the sorted classes of Provider
|
||||
private final List<Class<? extends S>> sortedClassList = Collections.synchronizedList(new ArrayList<Class<? extends S>>());
|
||||
|
||||
/**
|
||||
* Cache the classes of Provider, key: aliasName, value: class of Provider.
|
||||
* Note: aliasName is the value of {@link Spi} when the Provider class has {@link Spi} annotation and value is not empty,
|
||||
* otherwise use classname of the Provider.
|
||||
*/
|
||||
private final ConcurrentHashMap<String, Class<? extends S>> classMap = new ConcurrentHashMap<>();
|
||||
|
||||
// Cache the singleton instance of Provider, key: classname of Provider, value: Provider instance
|
||||
private final ConcurrentHashMap<String, S> singletonMap = new ConcurrentHashMap<>();
|
||||
|
||||
// Whether this SpiLoader has beend loaded, that is, loaded the Provider configuration file
|
||||
private final AtomicBoolean loaded = new AtomicBoolean(false);
|
||||
|
||||
// Default provider class
|
||||
private Class<? extends S> defaultClass = null;
|
||||
|
||||
// The Service class, must be interface or abstract class
|
||||
private Class<S> service;
|
||||
|
||||
/**
|
||||
* Create SpiLoader instance via Service class
|
||||
* Cached by className, and load from cache first
|
||||
*
|
||||
* @param service Service class
|
||||
* @param <T> Service type
|
||||
* @return SpiLoader instance
|
||||
*/
|
||||
public static <T> SpiLoader<T> of(Class<T> service) {
|
||||
AssertUtil.notNull(service, "SPI class cannot be null");
|
||||
AssertUtil.isTrue(service.isInterface() || Modifier.isAbstract(service.getModifiers()),
|
||||
"SPI class[" + service.getName() + "] must be interface or abstract class");
|
||||
|
||||
String className = service.getName();
|
||||
SpiLoader<T> spiLoader = SPI_LOADER_MAP.get(className);
|
||||
if (spiLoader == null) {
|
||||
synchronized (SpiLoader.class) {
|
||||
spiLoader = SPI_LOADER_MAP.get(className);
|
||||
if (spiLoader == null) {
|
||||
SPI_LOADER_MAP.putIfAbsent(className, new SpiLoader<>(service));
|
||||
spiLoader = SPI_LOADER_MAP.get(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spiLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset and clear all SpiLoader instances.
|
||||
* Package privilege, used only in test cases.
|
||||
*/
|
||||
synchronized static void resetAndClearAll() {
|
||||
Set<Map.Entry<String, SpiLoader>> entries = SPI_LOADER_MAP.entrySet();
|
||||
for (Map.Entry<String, SpiLoader> entry : entries) {
|
||||
SpiLoader spiLoader = entry.getValue();
|
||||
spiLoader.resetAndClear();
|
||||
}
|
||||
SPI_LOADER_MAP.clear();
|
||||
}
|
||||
|
||||
// Private access
|
||||
private SpiLoader(Class<S> service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all Provider instances of the specified Service
|
||||
*
|
||||
* @return Provider instances list
|
||||
*/
|
||||
public List<S> loadInstanceList() {
|
||||
load();
|
||||
|
||||
return createInstanceList(classList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all Provider instances of the specified Service, sorted by order value in class's {@link Spi} annotation
|
||||
*
|
||||
* @return Sorted Provider instances list
|
||||
*/
|
||||
public List<S> loadInstanceListSorted() {
|
||||
load();
|
||||
|
||||
return createInstanceList(sortedClassList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load highest order priority instance, order value is defined in class's {@link Spi} annotation
|
||||
*
|
||||
* @return Provider instance of highest order priority
|
||||
*/
|
||||
public S loadHighestPriorityInstance() {
|
||||
load();
|
||||
|
||||
if (sortedClassList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends S> highestClass = sortedClassList.get(0);
|
||||
return createInstance(highestClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load lowest order priority instance, order value is defined in class's {@link Spi} annotation
|
||||
*
|
||||
* @return Provider instance of lowest order priority
|
||||
*/
|
||||
public S loadLowestPriorityInstance() {
|
||||
load();
|
||||
|
||||
if (sortedClassList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends S> lowestClass = sortedClassList.get(sortedClassList.size() - 1);
|
||||
return createInstance(lowestClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the first-found Provider instance
|
||||
*
|
||||
* @return Provider instance of first-found specific
|
||||
*/
|
||||
public S loadFirstInstance() {
|
||||
load();
|
||||
|
||||
if (classList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends S> serviceClass = classList.get(0);
|
||||
S instance = createInstance(serviceClass);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the first-found Provider instance,if not found, return default Provider instance
|
||||
*
|
||||
* @return Provider instance
|
||||
*/
|
||||
public S loadFirstInstanceOrDefault() {
|
||||
load();
|
||||
|
||||
for (Class<? extends S> clazz : classList) {
|
||||
if (defaultClass == null || clazz != defaultClass) {
|
||||
return createInstance(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
return loadDefaultInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load default Provider instance
|
||||
* Provider class with @Spi(isDefault = true)
|
||||
*
|
||||
* @return default Provider instance
|
||||
*/
|
||||
public S loadDefaultInstance() {
|
||||
load();
|
||||
|
||||
if (defaultClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createInstance(defaultClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load instance by specific class type
|
||||
*
|
||||
* @param clazz class type
|
||||
* @return Provider instance
|
||||
*/
|
||||
public S loadInstance(Class<? extends S> clazz) {
|
||||
AssertUtil.notNull(clazz, "SPI class cannot be null");
|
||||
|
||||
if (clazz.equals(service)) {
|
||||
fail(clazz.getName() + " is not subtype of " + service.getName());
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
if (!classMap.containsValue(clazz)) {
|
||||
fail(clazz.getName() + " is not Provider class of " + service.getName() + ",check if it is in the SPI configuration file?");
|
||||
}
|
||||
|
||||
return createInstance(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load instance by aliasName of Provider class
|
||||
*
|
||||
* @param aliasName aliasName of Provider class
|
||||
* @return Provider instance
|
||||
*/
|
||||
public S loadInstance(String aliasName) {
|
||||
AssertUtil.notEmpty(aliasName, "aliasName cannot be empty");
|
||||
|
||||
load();
|
||||
|
||||
Class<? extends S> clazz = classMap.get(aliasName);
|
||||
if (clazz == null) {
|
||||
fail("no Provider class's aliasName is " + aliasName);
|
||||
}
|
||||
|
||||
return createInstance(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset and clear all fields of current SpiLoader instance and remove instance in SPI_LOADER_MAP
|
||||
*/
|
||||
synchronized void resetAndClear() {
|
||||
SPI_LOADER_MAP.remove(service.getName());
|
||||
classList.clear();
|
||||
sortedClassList.clear();
|
||||
classMap.clear();
|
||||
singletonMap.clear();
|
||||
defaultClass = null;
|
||||
loaded.set(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Provider class from Provider configuration file
|
||||
*/
|
||||
public void load() {
|
||||
if (!loaded.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String fullFileName = SPI_FILE_PREFIX + service.getName();
|
||||
ClassLoader classLoader;
|
||||
if (SentinelConfig.shouldUseContextClassloader()) {
|
||||
classLoader = Thread.currentThread().getContextClassLoader();
|
||||
} else {
|
||||
classLoader = service.getClassLoader();
|
||||
}
|
||||
if (classLoader == null) {
|
||||
classLoader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
Enumeration<URL> urls = null;
|
||||
try {
|
||||
urls = classLoader.getResources(fullFileName);
|
||||
} catch (IOException e) {
|
||||
fail("Error locating SPI configuration file,filename=" + fullFileName + ",classloader=" + classLoader, e);
|
||||
}
|
||||
|
||||
if (urls == null || !urls.hasMoreElements()) {
|
||||
RecordLog.warn("No SPI configuration file,filename=" + fullFileName + ",classloader=" + classLoader);
|
||||
return;
|
||||
}
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
|
||||
InputStream in = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
in = url.openStream();
|
||||
br = new BufferedReader(new InputStreamReader(in, "utf-8"));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (StringUtil.isBlank(line)) {
|
||||
// Skip blank line
|
||||
continue;
|
||||
}
|
||||
|
||||
line = line.trim();
|
||||
int commentIndex = line.indexOf("#");
|
||||
if (commentIndex == 0) {
|
||||
// Skip comment line
|
||||
continue;
|
||||
}
|
||||
|
||||
if (commentIndex > 0) {
|
||||
line = line.substring(0, commentIndex);
|
||||
}
|
||||
line = line.trim();
|
||||
|
||||
Class<S> clazz = null;
|
||||
try {
|
||||
clazz = (Class<S>) Class.forName(line, false, classLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
fail("class " + line + " not found", e);
|
||||
}
|
||||
|
||||
if (!service.isAssignableFrom(clazz)) {
|
||||
fail("class " + clazz.getName() + "is not subtype of " + service.getName() + ",SPI configuration file=" + fullFileName);
|
||||
}
|
||||
|
||||
classList.add(clazz);
|
||||
Spi spi = clazz.getAnnotation(Spi.class);
|
||||
String aliasName = spi == null || "".equals(spi.value()) ? clazz.getName() : spi.value();
|
||||
if (classMap.containsKey(aliasName)) {
|
||||
Class<? extends S> existClass = classMap.get(aliasName);
|
||||
fail("Found repeat aliasname for " + clazz.getName() + " and "
|
||||
+ existClass.getName() + ",SPI configuration file=" + fullFileName);
|
||||
}
|
||||
classMap.put(aliasName, clazz);
|
||||
|
||||
if (spi != null && spi.isDefault()) {
|
||||
if (defaultClass != null) {
|
||||
fail("Found more than one default Provider,SPI configuration file=" + fullFileName);
|
||||
}
|
||||
defaultClass = clazz;
|
||||
}
|
||||
|
||||
RecordLog.info("[SpiLoader]Found SPI,Service={},Provider={},aliasname={},isSingleton={},isDefault={},order={}",
|
||||
service.getName(), line, aliasName
|
||||
, spi == null ? true : spi.isSingleton()
|
||||
, spi == null ? false : spi.isDefault()
|
||||
, spi == null ? 0 : spi.order());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fail("error reading SPI configuration file", e);
|
||||
} finally {
|
||||
closeResources(in, br);
|
||||
}
|
||||
}
|
||||
|
||||
sortedClassList.addAll(classList);
|
||||
Collections.sort(sortedClassList, new Comparator<Class<? extends S>>() {
|
||||
@Override
|
||||
public int compare(Class<? extends S> o1, Class<? extends S> o2) {
|
||||
Spi spi1 = o1.getAnnotation(Spi.class);
|
||||
int order1 = spi1 == null ? 0 : spi1.order();
|
||||
|
||||
Spi spi2 = o2.getAnnotation(Spi.class);
|
||||
int order2 = spi2 == null ? 0 : spi2.order();
|
||||
|
||||
return Integer.compare(order1, order2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "com.alibaba.csp.sentinel.spi.SpiLoader[" + service.getName() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Provider instance list
|
||||
*
|
||||
* @param clazzList class types of Providers
|
||||
* @return Provider instance list
|
||||
*/
|
||||
private List<S> createInstanceList(List<Class<? extends S>> clazzList) {
|
||||
if (clazzList == null || clazzList.size() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<S> instances = new ArrayList<>(clazzList.size());
|
||||
for (Class<? extends S> clazz : clazzList) {
|
||||
S instance = createInstance(clazz);
|
||||
instances.add(instance);
|
||||
}
|
||||
return instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Provider instance
|
||||
*
|
||||
* @param clazz class type of Provider
|
||||
* @return Provider class
|
||||
*/
|
||||
private S createInstance(Class<? extends S> clazz) {
|
||||
Spi spi = clazz.getAnnotation(Spi.class);
|
||||
boolean singleton = true;
|
||||
if (spi != null) {
|
||||
singleton = spi.isSingleton();
|
||||
}
|
||||
return createInstance(clazz, singleton);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Provider instance
|
||||
*
|
||||
* @param clazz class type of Provider
|
||||
* @param singleton if instance is singleton or prototype
|
||||
* @return Provider instance
|
||||
*/
|
||||
private S createInstance(Class<? extends S> clazz, boolean singleton) {
|
||||
S instance = null;
|
||||
try {
|
||||
if (singleton) {
|
||||
instance = singletonMap.get(clazz.getName());
|
||||
if (instance == null) {
|
||||
synchronized (this) {
|
||||
instance = singletonMap.get(clazz.getName());
|
||||
if (instance == null) {
|
||||
instance = service.cast(clazz.newInstance());
|
||||
singletonMap.put(clazz.getName(), instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instance = service.cast(clazz.newInstance());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
fail(clazz.getName() + " could not be instantiated");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all resources
|
||||
*
|
||||
* @param closeables {@link Closeable} resources
|
||||
*/
|
||||
private void closeResources(Closeable... closeables) {
|
||||
if (closeables == null || closeables.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Exception firstException = null;
|
||||
for (Closeable closeable : closeables) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Exception e) {
|
||||
if (firstException != null) {
|
||||
firstException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstException != null) {
|
||||
fail("error closing resources", firstException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw {@link SpiLoaderException} with message
|
||||
*
|
||||
* @param msg error message
|
||||
*/
|
||||
private void fail(String msg) {
|
||||
RecordLog.error(msg);
|
||||
throw new SpiLoaderException("[" + service.getName() + "]" + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw {@link SpiLoaderException} with message and Throwable
|
||||
*
|
||||
* @param msg error message
|
||||
*/
|
||||
private void fail(String msg, Throwable e) {
|
||||
RecordLog.error(msg, e);
|
||||
throw new SpiLoaderException("[" + service.getName() + "]" + msg, e);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* Error thrown when something goes wrong while loading Provider via {@link SpiLoader}.
|
||||
*
|
||||
* @author cdfive
|
||||
*/
|
||||
public class SpiLoaderException extends RuntimeException {
|
||||
|
||||
public SpiLoaderException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SpiLoaderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SpiLoaderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.alibaba.csp.sentinel.spi;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Documented
|
||||
public @interface SpiOrder {
|
||||
|
||||
/**
|
||||
* Represents the lowest precedence.
|
||||
*/
|
||||
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
|
||||
/**
|
||||
* Represents the highest precedence.
|
||||
*/
|
||||
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* The SPI precedence value. Lowest precedence by default.
|
||||
*
|
||||
* @return the precedence value
|
||||
*/
|
||||
int value() default LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public final class SpiLoader {
|
||||
|
||||
private static final Map<String, ServiceLoader> SERVICE_LOADER_MAP = new ConcurrentHashMap<String, ServiceLoader>();
|
||||
|
||||
/**
|
||||
* Load the first-found specific SPI instance
|
||||
*
|
||||
* @param clazz class of the SPI interface
|
||||
* @param <T> SPI type
|
||||
* @return the first specific SPI instance if exists, or else return null
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public static <T> T loadFirstInstance(Class<T> clazz) {
|
||||
AssertUtil.notNull(clazz, "SPI class cannot be null");
|
||||
try {
|
||||
String key = clazz.getName();
|
||||
// Not thread-safe, as it's expected to be resolved in a thread-safe context.
|
||||
ServiceLoader<T> serviceLoader = SERVICE_LOADER_MAP.get(key);
|
||||
if (serviceLoader == null) {
|
||||
serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);
|
||||
SERVICE_LOADER_MAP.put(key, serviceLoader);
|
||||
}
|
||||
|
||||
Iterator<T> iterator = serviceLoader.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
return iterator.next();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
RecordLog.error("[SpiLoader] ERROR: loadFirstInstance failed", t);
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the first-found specific SPI instance (excluding provided default SPI class).
|
||||
* If no other SPI implementation found, then create a default SPI instance.
|
||||
*
|
||||
* @param clazz class of the SPI interface
|
||||
* @param defaultClass class of the default SPI implementation (if no other implementation found)
|
||||
* @param <T> SPI type
|
||||
* @return the first specific SPI instance if exists, or else the default SPI instance
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public static <T> T loadFirstInstanceOrDefault(Class<T> clazz, Class<? extends T> defaultClass) {
|
||||
AssertUtil.notNull(clazz, "SPI class cannot be null");
|
||||
AssertUtil.notNull(defaultClass, "default SPI class cannot be null");
|
||||
try {
|
||||
String key = clazz.getName();
|
||||
// Not thread-safe, as it's expected to be resolved in a thread-safe context.
|
||||
ServiceLoader<T> serviceLoader = SERVICE_LOADER_MAP.get(key);
|
||||
if (serviceLoader == null) {
|
||||
serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);
|
||||
SERVICE_LOADER_MAP.put(key, serviceLoader);
|
||||
}
|
||||
|
||||
for (T instance : serviceLoader) {
|
||||
if (instance.getClass() != defaultClass) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
return defaultClass.newInstance();
|
||||
} catch (Throwable t) {
|
||||
RecordLog.error("[SpiLoader] ERROR: loadFirstInstanceOrDefault failed", t);
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the SPI instance with highest priority.
|
||||
*
|
||||
* Note: each call return same instances.
|
||||
*
|
||||
* @param clazz class of the SPI
|
||||
* @param <T> SPI type
|
||||
* @return the SPI instance with highest priority if exists, or else false
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static <T> T loadHighestPriorityInstance(Class<T> clazz) {
|
||||
try {
|
||||
String key = clazz.getName();
|
||||
// Not thread-safe, as it's expected to be resolved in a thread-safe context.
|
||||
ServiceLoader<T> serviceLoader = SERVICE_LOADER_MAP.get(key);
|
||||
if (serviceLoader == null) {
|
||||
serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);
|
||||
SERVICE_LOADER_MAP.put(key, serviceLoader);
|
||||
}
|
||||
|
||||
SpiOrderWrapper<T> w = null;
|
||||
for (T spi : serviceLoader) {
|
||||
int order = SpiOrderResolver.resolveOrder(spi);
|
||||
RecordLog.info("[SpiLoader] Found {} SPI: {} with order {}", clazz.getSimpleName(),
|
||||
spi.getClass().getCanonicalName(), order);
|
||||
if (w == null || order < w.order) {
|
||||
w = new SpiOrderWrapper<>(order, spi);
|
||||
}
|
||||
}
|
||||
return w == null ? null : w.spi;
|
||||
} catch (Throwable t) {
|
||||
RecordLog.error("[SpiLoader] ERROR: loadHighestPriorityInstance failed", t);
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and sorted SPI instance list.
|
||||
* Load the SPI instance list for provided SPI interface.
|
||||
*
|
||||
* Note: each call return same instances.
|
||||
*
|
||||
* @param clazz class of the SPI
|
||||
* @param <T> SPI type
|
||||
* @return sorted SPI instance list
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static <T> List<T> loadInstanceList(Class<T> clazz) {
|
||||
try {
|
||||
String key = clazz.getName();
|
||||
// Not thread-safe, as it's expected to be resolved in a thread-safe context.
|
||||
ServiceLoader<T> serviceLoader = SERVICE_LOADER_MAP.get(key);
|
||||
if (serviceLoader == null) {
|
||||
serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);
|
||||
SERVICE_LOADER_MAP.put(key, serviceLoader);
|
||||
}
|
||||
|
||||
List<T> list = new ArrayList<>();
|
||||
for (T spi : serviceLoader) {
|
||||
RecordLog.info("[SpiLoader] Found {} SPI: {}", clazz.getSimpleName(),
|
||||
spi.getClass().getCanonicalName());
|
||||
list.add(spi);
|
||||
}
|
||||
return list;
|
||||
} catch (Throwable t) {
|
||||
RecordLog.error("[SpiLoader] ERROR: loadInstanceList failed", t);
|
||||
t.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the sorted SPI instance list for provided SPI interface.
|
||||
*
|
||||
* Note: each call return same instances.
|
||||
*
|
||||
* @param clazz class of the SPI
|
||||
* @param <T> SPI type
|
||||
* @return sorted SPI instance list
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static <T> List<T> loadInstanceListSorted(Class<T> clazz) {
|
||||
try {
|
||||
String key = clazz.getName();
|
||||
// Not thread-safe, as it's expected to be resolved in a thread-safe context.
|
||||
ServiceLoader<T> serviceLoader = SERVICE_LOADER_MAP.get(key);
|
||||
if (serviceLoader == null) {
|
||||
serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);
|
||||
SERVICE_LOADER_MAP.put(key, serviceLoader);
|
||||
}
|
||||
|
||||
List<SpiOrderWrapper<T>> orderWrappers = new ArrayList<>();
|
||||
for (T spi : serviceLoader) {
|
||||
int order = SpiOrderResolver.resolveOrder(spi);
|
||||
// Since SPI is lazy initialized in ServiceLoader, we use online sort algorithm here.
|
||||
SpiOrderResolver.insertSorted(orderWrappers, spi, order);
|
||||
RecordLog.info("[SpiLoader] Found {} SPI: {} with order {}", clazz.getSimpleName(),
|
||||
spi.getClass().getCanonicalName(), order);
|
||||
}
|
||||
List<T> list = new ArrayList<>(orderWrappers.size());
|
||||
for (int i = 0; i < orderWrappers.size(); i++) {
|
||||
list.add(orderWrappers.get(i).spi);
|
||||
}
|
||||
return list;
|
||||
} catch (Throwable t) {
|
||||
RecordLog.error("[SpiLoader] ERROR: loadInstanceListSorted failed", t);
|
||||
t.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the sorted and prototype SPI instance list for provided SPI interface.
|
||||
*
|
||||
* Note: each call return different instances, i.e. prototype instance, not singleton instance.
|
||||
*
|
||||
* @param clazz class of the SPI
|
||||
* @param <T> SPI type
|
||||
* @return sorted and different SPI instance list
|
||||
* @since 1.7.2
|
||||
*/
|
||||
public static <T> List<T> loadPrototypeInstanceListSorted(Class<T> clazz) {
|
||||
try {
|
||||
// Not use SERVICE_LOADER_MAP, to make sure the instances loaded are different.
|
||||
ServiceLoader<T> serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);
|
||||
|
||||
List<SpiOrderWrapper<T>> orderWrappers = new ArrayList<>();
|
||||
for (T spi : serviceLoader) {
|
||||
int order = SpiOrderResolver.resolveOrder(spi);
|
||||
// Since SPI is lazy initialized in ServiceLoader, we use online sort algorithm here.
|
||||
SpiOrderResolver.insertSorted(orderWrappers, spi, order);
|
||||
RecordLog.debug("[SpiLoader] Found {} SPI: {} with order {}", clazz.getSimpleName(),
|
||||
spi.getClass().getCanonicalName(), order);
|
||||
}
|
||||
List<T> list = new ArrayList<>(orderWrappers.size());
|
||||
for (int i = 0; i < orderWrappers.size(); i++) {
|
||||
list.add(orderWrappers.get(i).spi);
|
||||
}
|
||||
return list;
|
||||
} catch (Throwable t) {
|
||||
RecordLog.error("[SpiLoader] ERROR: loadPrototypeInstanceListSorted failed", t);
|
||||
t.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SpiOrderResolver {
|
||||
private static <T> void insertSorted(List<SpiOrderWrapper<T>> list, T spi, int order) {
|
||||
int idx = 0;
|
||||
for (; idx < list.size(); idx++) {
|
||||
if (list.get(idx).getOrder() > order) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
list.add(idx, new SpiOrderWrapper<>(order, spi));
|
||||
}
|
||||
|
||||
private static <T> int resolveOrder(T spi) {
|
||||
if (!spi.getClass().isAnnotationPresent(SpiOrder.class)) {
|
||||
// Lowest precedence by default.
|
||||
return SpiOrder.LOWEST_PRECEDENCE;
|
||||
} else {
|
||||
return spi.getClass().getAnnotation(SpiOrder.class).value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SpiOrderWrapper<T> {
|
||||
private final int order;
|
||||
private final T spi;
|
||||
|
||||
SpiOrderWrapper(int order, T spi) {
|
||||
this.order = order;
|
||||
this.spi = spi;
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
T getSpi() {
|
||||
return spi;
|
||||
}
|
||||
}
|
||||
|
||||
private SpiLoader() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.spi;
|
||||
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.init.InitFunc;
|
||||
import com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit;
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import com.alibaba.csp.sentinel.slots.logger.LogSlot;
|
||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
|
||||
import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemSlot;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* Test cases for {@link SpiLoader}.
|
||||
*
|
||||
* @author cdfive
|
||||
*/
|
||||
public class SpiLoaderTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SpiLoader.resetAndClearAll();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void tearDown() {
|
||||
SpiLoader.resetAndClearAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSpiLoader() {
|
||||
SpiLoader slotLoader1 = SpiLoader.of(ProcessorSlot.class);
|
||||
assertNotNull(slotLoader1);
|
||||
|
||||
SpiLoader slotLoader2 = SpiLoader.of(ProcessorSlot.class);
|
||||
assertNotNull(slotLoader2);
|
||||
|
||||
assertSame(slotLoader1, slotLoader2);
|
||||
|
||||
SpiLoader initFuncLoader1 = SpiLoader.of(InitFunc.class);
|
||||
assertNotNull(initFuncLoader1);
|
||||
assertNotSame(slotLoader1, initFuncLoader1);
|
||||
assertNotEquals(slotLoader1, initFuncLoader1);
|
||||
|
||||
SpiLoader<InitFunc> initFuncLoader2 = SpiLoader.of(InitFunc.class);
|
||||
assertNotNull(initFuncLoader2);
|
||||
|
||||
assertSame(initFuncLoader1, initFuncLoader2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSpiLoaderNotInterface() {
|
||||
try {
|
||||
SpiLoader.of(SphU.class);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
assertThat(e.getMessage(), containsString("must be interface or abstract class"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInstanceList() {
|
||||
SpiLoader spiLoader = SpiLoader.of(ProcessorSlot.class);
|
||||
List<ProcessorSlot> slots1 = spiLoader.loadInstanceList();
|
||||
List<ProcessorSlot> slots2 = spiLoader.loadInstanceList();
|
||||
assertNotSame(slots1, slots2);
|
||||
|
||||
List<Class<? extends ProcessorSlot>> prototypeSlotClasses = new ArrayList<>(2);
|
||||
prototypeSlotClasses.add(NodeSelectorSlot.class);
|
||||
prototypeSlotClasses.add(ClusterBuilderSlot.class);
|
||||
|
||||
List<Class<? extends ProcessorSlot>> singletonSlotClasses = new ArrayList<>(6);
|
||||
singletonSlotClasses.add(LogSlot.class);
|
||||
singletonSlotClasses.add(StatisticSlot.class);
|
||||
singletonSlotClasses.add(AuthoritySlot.class);
|
||||
singletonSlotClasses.add(SystemSlot.class);
|
||||
singletonSlotClasses.add(FlowSlot.class);
|
||||
singletonSlotClasses.add(DegradeSlot.class);
|
||||
|
||||
for (int i = 0; i < slots1.size(); i++) {
|
||||
ProcessorSlot slot1 = slots1.get(i);
|
||||
ProcessorSlot slot2 = slots2.get(i);
|
||||
assertSame(slot1.getClass(), slot2.getClass());
|
||||
|
||||
boolean found = false;
|
||||
for (Class<? extends ProcessorSlot> prototypeSlotClass : prototypeSlotClasses) {
|
||||
if (prototypeSlotClass.equals(slot1.getClass())) {
|
||||
found = true;
|
||||
assertTrue(prototypeSlotClass.equals(slot2.getClass()));
|
||||
// Verify prototype function
|
||||
assertNotSame(slot1, slot2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Class<? extends ProcessorSlot> singletonSlotClass : singletonSlotClasses) {
|
||||
if (singletonSlotClass.equals(slot1.getClass())) {
|
||||
found = true;
|
||||
assertTrue(singletonSlotClass.equals(slot2.getClass()));
|
||||
// Verify single function
|
||||
assertSame(slot1, slot2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fail("Should found and not go through here");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInstanceListSorted() {
|
||||
List<ProcessorSlot> sortedSlots = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
|
||||
assertNotNull(sortedSlots);
|
||||
|
||||
// Total 8 default slot in sentinel-core
|
||||
assertEquals(8, sortedSlots.size());
|
||||
|
||||
// Verify the order of slot
|
||||
int index = 0;
|
||||
assertTrue(sortedSlots.get(index++) instanceof NodeSelectorSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof ClusterBuilderSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof LogSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof StatisticSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof SystemSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof FlowSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof DegradeSlot);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadHighestPriorityInstance() {
|
||||
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadHighestPriorityInstance();
|
||||
assertNotNull(slot);
|
||||
|
||||
// NodeSelectorSlot is highest order priority with @Spi(order = -10000) among all slots
|
||||
assertTrue(slot instanceof NodeSelectorSlot);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadLowestPriorityInstance() {
|
||||
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadLowestPriorityInstance();
|
||||
assertNotNull(slot);
|
||||
|
||||
// NodeSelectorSlot is lowest order priority with @Spi(order = -1000) among all slots
|
||||
assertTrue(slot instanceof DegradeSlot);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadFirstInstance() {
|
||||
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadFirstInstance();
|
||||
assertNotNull(slot);
|
||||
assertTrue(slot instanceof NodeSelectorSlot);
|
||||
|
||||
SlotChainBuilder chainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstance();
|
||||
assertNotNull(chainBuilder);
|
||||
assertTrue(chainBuilder instanceof SlotChainBuilder);
|
||||
|
||||
InitFunc initFunc = SpiLoader.of(InitFunc.class).loadFirstInstance();
|
||||
assertNotNull(initFunc);
|
||||
assertTrue(initFunc instanceof MetricCallbackInit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadFirstInstanceOrDefault() {
|
||||
SlotChainBuilder slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
|
||||
assertNotNull(slotChainBuilder);
|
||||
assertTrue(slotChainBuilder instanceof DefaultSlotChainBuilder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDefaultInstance() {
|
||||
SlotChainBuilder slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadDefaultInstance();
|
||||
assertNotNull(slotChainBuilder);
|
||||
assertTrue(slotChainBuilder instanceof DefaultSlotChainBuilder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInstanceByClass() {
|
||||
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadInstance(StatisticSlot.class);
|
||||
assertNotNull(slot);
|
||||
assertTrue(slot instanceof StatisticSlot);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInstanceByAliasName() {
|
||||
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadInstance("com.alibaba.csp.sentinel.slots.statistic.StatisticSlot");
|
||||
assertNotNull(slot);
|
||||
assertTrue(slot instanceof StatisticSlot);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
SpiLoader spiLoader = SpiLoader.of(ProcessorSlot.class);
|
||||
assertEquals("com.alibaba.csp.sentinel.spi.SpiLoader[com.alibaba.csp.sentinel.slotchain.ProcessorSlot]"
|
||||
, spiLoader.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Following test cases are for some test Interfaces.
|
||||
*/
|
||||
@Test
|
||||
public void test_TestNoSpiFileInterface() {
|
||||
SpiLoader<TestNoSpiFileInterface> loader = SpiLoader.of(TestNoSpiFileInterface.class);
|
||||
|
||||
List<TestNoSpiFileInterface> providers = loader.loadInstanceList();
|
||||
assertTrue(providers.size() == 0);
|
||||
|
||||
List<TestNoSpiFileInterface> sortedProviders = loader.loadInstanceListSorted();
|
||||
assertTrue(sortedProviders.size() == 0);
|
||||
|
||||
TestNoSpiFileInterface firstProvider = loader.loadFirstInstance();
|
||||
assertNull(firstProvider);
|
||||
|
||||
TestNoSpiFileInterface defaultProvider = loader.loadDefaultInstance();
|
||||
assertNull(defaultProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_TestNoProviderInterface() {
|
||||
List<TestNoProviderInterface> providers = SpiLoader.of(TestNoProviderInterface.class).loadInstanceList();
|
||||
assertTrue(providers.size() == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_TestInterface() {
|
||||
SpiLoader<TestInterface> loader = SpiLoader.of(TestInterface.class);
|
||||
|
||||
List<TestInterface> providers = loader.loadInstanceList();
|
||||
assertTrue(providers.size() == 4);
|
||||
assertTrue(providers.get(0) instanceof TestOneProvider);
|
||||
assertTrue(providers.get(1) instanceof TestTwoProvider);
|
||||
assertTrue(providers.get(2) instanceof TestThreeProvider);
|
||||
assertTrue(providers.get(3) instanceof TestFiveProvider);
|
||||
|
||||
List<TestInterface> sortedProviders = loader.loadInstanceListSorted();
|
||||
assertEquals(sortedProviders.size(), 4);
|
||||
assertTrue(sortedProviders.get(0) instanceof TestThreeProvider);
|
||||
assertTrue(sortedProviders.get(1) instanceof TestFiveProvider);
|
||||
assertTrue(sortedProviders.get(2) instanceof TestTwoProvider);
|
||||
assertTrue(sortedProviders.get(3) instanceof TestOneProvider);
|
||||
|
||||
assertSame(providers.get(0), sortedProviders.get(3));
|
||||
assertSame(providers.get(1), sortedProviders.get(2));
|
||||
assertNotSame(providers.get(2), sortedProviders.get(0));
|
||||
assertSame(providers.get(3), sortedProviders.get(1));
|
||||
|
||||
assertTrue(loader.loadDefaultInstance() instanceof TestFiveProvider);
|
||||
|
||||
assertTrue(loader.loadHighestPriorityInstance() instanceof TestThreeProvider);
|
||||
assertTrue(loader.loadLowestPriorityInstance() instanceof TestOneProvider);
|
||||
|
||||
assertTrue(loader.loadInstance("two") instanceof TestTwoProvider);
|
||||
assertSame(loader.loadInstance("two"), loader.loadInstance("two"));
|
||||
|
||||
try {
|
||||
loader.loadInstance("one");
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof SpiLoaderException);
|
||||
assertThat(e.getMessage(), containsString("no Provider class's aliasName is one"));
|
||||
}
|
||||
|
||||
TestInterface oneProvider1 = loader.loadInstance(TestOneProvider.class);
|
||||
assertNotNull(oneProvider1);
|
||||
TestInterface oneProvider2 = loader.loadInstance(TestOneProvider.class);
|
||||
assertNotNull(oneProvider2);
|
||||
assertSame(oneProvider1, oneProvider2);
|
||||
|
||||
try {
|
||||
loader.loadInstance(TestInterface.class);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof SpiLoaderException);
|
||||
assertThat(e.getMessage(), containsString("is not subtype of"));
|
||||
}
|
||||
|
||||
try {
|
||||
loader.loadInstance(TestFourProvider.class);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof SpiLoaderException);
|
||||
assertThat(e.getMessage(), allOf(containsString("is not Provider class of")
|
||||
, containsString("check if it is in the SPI configuration file?")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
@Spi(value = "five", isDefault = true, order = -270)
|
||||
public class TestFiveProvider implements TestInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* This Provider class isn't configured in SPI file.
|
||||
*
|
||||
* @author cdfive
|
||||
*/
|
||||
@Spi(value = "four", isSingleton = true, order = -400)
|
||||
public class TestFourProvider implements TestInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public interface TestInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public interface TestNoProviderInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public interface TestNoSpiFileInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public class TestOneProvider implements TestInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
@Spi(order = -300, isSingleton = false)
|
||||
public class TestThreeProvider implements TestInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.alibaba.csp.sentinel.spi;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
@Spi(value = "two", isSingleton = true, order = -200)
|
||||
public class TestTwoProvider implements TestInterface {
|
||||
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.alibaba.csp.sentinel.util;
|
||||
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import com.alibaba.csp.sentinel.slots.logger.LogSlot;
|
||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
|
||||
import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemSlot;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test cases for {@link SpiLoader}.
|
||||
*
|
||||
* @author cdfive
|
||||
*/
|
||||
public class SpiLoaderTest {
|
||||
|
||||
@Test
|
||||
public void testLoadFirstInstance() {
|
||||
ProcessorSlot processorSlot = SpiLoader.loadFirstInstance(ProcessorSlot.class);
|
||||
assertNotNull(processorSlot);
|
||||
|
||||
ProcessorSlot processorSlot2 = SpiLoader.loadFirstInstance(ProcessorSlot.class);
|
||||
// As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances
|
||||
assertSame(processorSlot, processorSlot2);
|
||||
|
||||
SlotChainBuilder slotChainBuilder = SpiLoader.loadFirstInstance(SlotChainBuilder.class);
|
||||
assertNotNull(slotChainBuilder);
|
||||
assertTrue(slotChainBuilder instanceof DefaultSlotChainBuilder);
|
||||
|
||||
SlotChainBuilder slotChainBuilder2 = SpiLoader.loadFirstInstance(SlotChainBuilder.class);
|
||||
// As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances
|
||||
assertSame(slotChainBuilder, slotChainBuilder2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadHighestPriorityInstance() {
|
||||
ProcessorSlot processorSlot = SpiLoader.loadHighestPriorityInstance(ProcessorSlot.class);
|
||||
assertNotNull(processorSlot);
|
||||
|
||||
// NodeSelectorSlot is highest order with @SpiOrder(-10000), among all slots
|
||||
assertTrue(processorSlot instanceof NodeSelectorSlot);
|
||||
|
||||
ProcessorSlot processorSlot2 = SpiLoader.loadHighestPriorityInstance(ProcessorSlot.class);
|
||||
// As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances
|
||||
assertSame(processorSlot, processorSlot2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInstanceList() {
|
||||
List<ProcessorSlot> slots = SpiLoader.loadInstanceList(ProcessorSlot.class);
|
||||
assertNotNull(slots);
|
||||
|
||||
// Total 8 default slot in sentinel-core
|
||||
assertEquals(8, slots.size());
|
||||
|
||||
// Get the first slot of slots
|
||||
ProcessorSlot firstSlot = slots.get(0);
|
||||
|
||||
// Call loadInstanceList again
|
||||
List<ProcessorSlot> slots2 = SpiLoader.loadInstanceList(ProcessorSlot.class);
|
||||
// Note: the return list are different, and the item instances in list are same
|
||||
assertNotSame(slots, slots2);
|
||||
|
||||
// Get the first slot of slots2
|
||||
ProcessorSlot firstSlot2 = slots2.get(0);
|
||||
|
||||
// As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances
|
||||
assertSame(firstSlot, firstSlot2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInstanceListSorted() {
|
||||
List<ProcessorSlot> sortedSlots = SpiLoader.loadInstanceListSorted(ProcessorSlot.class);
|
||||
assertNotNull(sortedSlots);
|
||||
|
||||
// Total 8 default slot in sentinel-core
|
||||
assertEquals(8, sortedSlots.size());
|
||||
|
||||
// Verify the order of slot
|
||||
int index = 0;
|
||||
assertTrue(sortedSlots.get(index++) instanceof NodeSelectorSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof ClusterBuilderSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof LogSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof StatisticSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof SystemSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof FlowSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof DegradeSlot);
|
||||
|
||||
// Verify each call return different instances
|
||||
// Note: the return list are different, and the item instances in list are same
|
||||
List<ProcessorSlot> sortedSlots2 = SpiLoader.loadInstanceListSorted(ProcessorSlot.class);
|
||||
assertNotSame(sortedSlots, sortedSlots2);
|
||||
assertEquals(sortedSlots.size(), sortedSlots2.size());
|
||||
for (int i = 0; i < sortedSlots.size(); i++) {
|
||||
ProcessorSlot slot = sortedSlots.get(i);
|
||||
ProcessorSlot slot2 = sortedSlots2.get(i);
|
||||
assertEquals(slot.getClass(), slot2.getClass());
|
||||
|
||||
// As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances
|
||||
assertSame(slot, slot2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadPrototypeInstanceListSorted() {
|
||||
List<ProcessorSlot> sortedSlots = SpiLoader.loadInstanceListSorted(ProcessorSlot.class);
|
||||
assertNotNull(sortedSlots);
|
||||
|
||||
// Total 8 default slot in sentinel-core
|
||||
assertEquals(8, sortedSlots.size());
|
||||
|
||||
// Verify the order of slot
|
||||
int index = 0;
|
||||
assertTrue(sortedSlots.get(index++) instanceof NodeSelectorSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof ClusterBuilderSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof LogSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof StatisticSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof SystemSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof FlowSlot);
|
||||
assertTrue(sortedSlots.get(index++) instanceof DegradeSlot);
|
||||
|
||||
// Verify each call return new instances
|
||||
List<ProcessorSlot> sortedSlots2 = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class);
|
||||
assertNotSame(sortedSlots, sortedSlots2);
|
||||
assertEquals(sortedSlots.size(), sortedSlots2.size());
|
||||
for (int i = 0; i < sortedSlots.size(); i++) {
|
||||
ProcessorSlot slot = sortedSlots.get(i);
|
||||
ProcessorSlot slot2 = sortedSlots2.get(i);
|
||||
assertEquals(slot.getClass(), slot2.getClass());
|
||||
|
||||
// Verify the instances are different
|
||||
assertNotSame(slot, slot2);
|
||||
assertNotEquals(slot, slot2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# One
|
||||
com.alibaba.csp.sentinel.spi.TestOneProvider
|
||||
com.alibaba.csp.sentinel.spi.TestTwoProvider # Two
|
||||
com.alibaba.csp.sentinel.spi.TestThreeProvider
|
||||
com.alibaba.csp.sentinel.spi.TestFiveProvider
|
||||
|
|
@ -19,14 +19,14 @@ import com.alibaba.csp.sentinel.context.Context;
|
|||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* An example slot that records current context and entry resource.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SpiOrder(-3500)
|
||||
@Spi(order = -3500)
|
||||
public class DemoSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot;
|
|||
* @author Eric Zhao
|
||||
* @since 0.2.0
|
||||
*
|
||||
* @deprecated since 1.7.2, we can use @SpiOrder(-3000) to adjust the order of {@link ParamFlowSlot},
|
||||
* @deprecated since 1.7.2, we can use @Spi(order = -3000) to adjust the order of {@link ParamFlowSlot},
|
||||
* this class is reserved for compatibility with older versions.
|
||||
*
|
||||
* @see ParamFlowSlot
|
||||
* @see DefaultSlotChainBuilder
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
|
|||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ import java.util.List;
|
|||
* @author Eric Zhao
|
||||
* @since 0.2.0
|
||||
*/
|
||||
@SpiOrder(-3000)
|
||||
@Spi(order = -3000)
|
||||
public class ParamFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.command;
|
|||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.transport.CommandCenter;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* Provider for a universal {@link CommandCenter} instance.
|
||||
|
|
@ -34,7 +34,7 @@ public final class CommandCenterProvider {
|
|||
}
|
||||
|
||||
private static void resolveInstance() {
|
||||
CommandCenter resolveCommandCenter = SpiLoader.loadHighestPriorityInstance(CommandCenter.class);
|
||||
CommandCenter resolveCommandCenter = SpiLoader.of(CommandCenter.class).loadHighestPriorityInstance();
|
||||
|
||||
if (resolveCommandCenter == null) {
|
||||
RecordLog.warn("[CommandCenterProvider] WARN: No existing CommandCenter found");
|
||||
|
|
|
|||
|
|
@ -15,13 +15,10 @@
|
|||
*/
|
||||
package com.alibaba.csp.sentinel.command;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.*;
|
||||
|
||||
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
|
||||
import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
|
|
@ -31,8 +28,7 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
|||
*/
|
||||
public class CommandHandlerProvider implements Iterable<CommandHandler> {
|
||||
|
||||
private final ServiceLoader<CommandHandler> serviceLoader = ServiceLoaderUtil.getServiceLoader(
|
||||
CommandHandler.class);
|
||||
private final SpiLoader<CommandHandler> spiLoader = SpiLoader.of(CommandHandler.class);
|
||||
|
||||
/**
|
||||
* Get all command handlers annotated with {@link CommandMapping} with command name.
|
||||
|
|
@ -41,7 +37,8 @@ public class CommandHandlerProvider implements Iterable<CommandHandler> {
|
|||
*/
|
||||
public Map<String, CommandHandler> namedHandlers() {
|
||||
Map<String, CommandHandler> map = new HashMap<String, CommandHandler>();
|
||||
for (CommandHandler handler : serviceLoader) {
|
||||
List<CommandHandler> handlers = spiLoader.loadInstanceList();
|
||||
for (CommandHandler handler : handlers) {
|
||||
String name = parseCommandName(handler);
|
||||
if (!StringUtil.isEmpty(name)) {
|
||||
map.put(name, handler);
|
||||
|
|
@ -61,7 +58,7 @@ public class CommandHandlerProvider implements Iterable<CommandHandler> {
|
|||
|
||||
@Override
|
||||
public Iterator<CommandHandler> iterator() {
|
||||
return serviceLoader.iterator();
|
||||
return spiLoader.loadInstanceList().iterator();
|
||||
}
|
||||
|
||||
private static final CommandHandlerProvider INSTANCE = new CommandHandlerProvider();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.heartbeat;
|
|||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -32,7 +32,7 @@ public final class HeartbeatSenderProvider {
|
|||
}
|
||||
|
||||
private static void resolveInstance() {
|
||||
HeartbeatSender resolved = SpiLoader.loadHighestPriorityInstance(HeartbeatSender.class);
|
||||
HeartbeatSender resolved = SpiLoader.of(HeartbeatSender.class).loadHighestPriorityInstance();
|
||||
if (resolved == null) {
|
||||
RecordLog.warn("[HeartbeatSenderProvider] WARN: No existing HeartbeatSender found");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import java.util.concurrent.Executors;
|
|||
import com.alibaba.csp.sentinel.command.CommandHandler;
|
||||
import com.alibaba.csp.sentinel.command.CommandHandlerProvider;
|
||||
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
import com.alibaba.csp.sentinel.transport.command.netty.HttpServer;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.transport.CommandCenter;
|
||||
|
|
@ -32,7 +32,7 @@ import com.alibaba.csp.sentinel.transport.CommandCenter;
|
|||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SpiOrder(SpiOrder.LOWEST_PRECEDENCE - 100)
|
||||
@Spi(order = Spi.ORDER_LOWEST - 100)
|
||||
public class NettyHttpCommandCenter implements CommandCenter {
|
||||
|
||||
private final HttpServer server = new HttpServer();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.transport.heartbeat;
|
|||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.spi.SpiOrder;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
import com.alibaba.csp.sentinel.transport.endpoint.Protocol;
|
||||
|
|
@ -42,7 +42,7 @@ import java.util.List;
|
|||
* @author Carpenter Lee
|
||||
* @author Leo Li
|
||||
*/
|
||||
@SpiOrder(SpiOrder.LOWEST_PRECEDENCE - 100)
|
||||
@Spi(order = Spi.ORDER_LOWEST - 100)
|
||||
public class HttpHeartbeatSender implements HeartbeatSender {
|
||||
|
||||
private final CloseableHttpClient client;
|
||||
|
|
|
|||
Loading…
Reference in New Issue