Quellcode durchsuchen

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.
master
cdfive GitHub vor 3 Jahren
Ursprung
Commit
62efb78d4e
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden GPG-Schlüssel-ID: 4AEE18F83AFDEB23
50 geänderte Dateien mit 1124 neuen und 619 gelöschten Zeilen
  1. +2
    -2
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
  2. +2
    -2
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java
  3. +2
    -1
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java
  4. +3
    -3
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java
  5. +2
    -0
      sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/DefaultTokenService.java
  6. +2
    -3
      sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/TokenServiceProvider.java
  7. +3
    -3
      sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/codec/ServerEntityCodecProvider.java
  8. +4
    -6
      sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/processor/RequestProcessorProvider.java
  9. +12
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
  10. +2
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java
  11. +2
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java
  12. +14
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java
  13. +3
    -3
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java
  14. +2
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
  15. +2
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java
  16. +4
    -3
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java
  17. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
  18. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java
  19. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java
  20. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java
  21. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java
  22. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java
  23. +2
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java
  24. +3
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java
  25. +0
    -45
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/ServiceLoaderUtil.java
  26. +54
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/Spi.java
  27. +534
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoader.java
  28. +36
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoaderException.java
  29. +0
    -48
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiOrder.java
  30. +0
    -291
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/SpiLoader.java
  31. +321
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java
  32. +9
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFiveProvider.java
  33. +11
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFourProvider.java
  34. +8
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestInterface.java
  35. +8
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoProviderInterface.java
  36. +8
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoSpiFileInterface.java
  37. +8
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestOneProvider.java
  38. +9
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestThreeProvider.java
  39. +9
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestTwoProvider.java
  40. +0
    -163
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/util/SpiLoaderTest.java
  41. +5
    -0
      sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestInterface
  42. +0
    -0
      sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestNoProviderInterface
  43. +2
    -2
      sentinel-demo/sentinel-demo-slot-chain-spi/src/main/java/com/alibaba/csp/sentinel/demo/slot/DemoSlot.java
  44. +2
    -1
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java
  45. +2
    -2
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java
  46. +2
    -2
      sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandCenterProvider.java
  47. +6
    -9
      sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java
  48. +2
    -2
      sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/heartbeat/HeartbeatSenderProvider.java
  49. +2
    -2
      sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/NettyHttpCommandCenter.java
  50. +2
    -2
      sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/heartbeat/HttpHeartbeatSender.java

+ 2
- 2
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java Datei anzeigen

@@ -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: {}"


+ 2
- 2
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java Datei anzeigen

@@ -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


+ 2
- 1
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java Datei anzeigen

@@ -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
*/


+ 3
- 3
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java Datei anzeigen

@@ -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 {


+ 2
- 0
sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/DefaultTokenService.java Datei anzeigen

@@ -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


+ 2
- 3
sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/TokenServiceProvider.java Datei anzeigen

@@ -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());


+ 3
- 3
sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/codec/ServerEntityCodecProvider.java Datei anzeigen

@@ -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 {


+ 4
- 6
sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/processor/RequestProcessorProvider.java Datei anzeigen

@@ -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);


+ 12
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java Datei anzeigen

@@ -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() {}
}

+ 2
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java Datei anzeigen

@@ -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");


+ 2
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java Datei anzeigen

@@ -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 {


+ 14
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java Datei anzeigen

@@ -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() {}
}

+ 3
- 3
sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java Datei anzeigen

@@ -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);
}


+ 2
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java Datei anzeigen

@@ -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");


+ 2
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java Datei anzeigen

@@ -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.


+ 4
- 3
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java Datei anzeigen

@@ -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");


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java Datei anzeigen

@@ -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


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java Datei anzeigen

@@ -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


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java Datei anzeigen

@@ -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;


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java Datei anzeigen

@@ -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> {

/**


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java Datei anzeigen

@@ -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


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java Datei anzeigen

@@ -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> {

/**


+ 2
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java Datei anzeigen

@@ -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


+ 3
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java Datei anzeigen

@@ -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


+ 0
- 45
sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/ServiceLoaderUtil.java Datei anzeigen

@@ -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() {}
}

+ 54
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/Spi.java Datei anzeigen

@@ -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;
}

+ 534
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoader.java Datei anzeigen

@@ -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);
}
}

+ 36
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoaderException.java Datei anzeigen

@@ -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);
}
}

+ 0
- 48
sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiOrder.java Datei anzeigen

@@ -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;
}

+ 0
- 291
sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/SpiLoader.java Datei anzeigen

@@ -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() {}
}

+ 321
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java Datei anzeigen

@@ -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?")));
}
}
}

+ 9
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFiveProvider.java Datei anzeigen

@@ -0,0 +1,9 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
@Spi(value = "five", isDefault = true, order = -270)
public class TestFiveProvider implements TestInterface {

}

+ 11
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFourProvider.java Datei anzeigen

@@ -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 {

}

+ 8
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestInterface.java Datei anzeigen

@@ -0,0 +1,8 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
public interface TestInterface {

}

+ 8
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoProviderInterface.java Datei anzeigen

@@ -0,0 +1,8 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
public interface TestNoProviderInterface {

}

+ 8
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoSpiFileInterface.java Datei anzeigen

@@ -0,0 +1,8 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
public interface TestNoSpiFileInterface {

}

+ 8
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestOneProvider.java Datei anzeigen

@@ -0,0 +1,8 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
public class TestOneProvider implements TestInterface {

}

+ 9
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestThreeProvider.java Datei anzeigen

@@ -0,0 +1,9 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
@Spi(order = -300, isSingleton = false)
public class TestThreeProvider implements TestInterface {

}

+ 9
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestTwoProvider.java Datei anzeigen

@@ -0,0 +1,9 @@
package com.alibaba.csp.sentinel.spi;

/**
* @author cdfive
*/
@Spi(value = "two", isSingleton = true, order = -200)
public class TestTwoProvider implements TestInterface {

}

+ 0
- 163
sentinel-core/src/test/java/com/alibaba/csp/sentinel/util/SpiLoaderTest.java Datei anzeigen

@@ -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);
}
}
}

+ 5
- 0
sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestInterface Datei anzeigen

@@ -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

+ 0
- 0
sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestNoProviderInterface Datei anzeigen


+ 2
- 2
sentinel-demo/sentinel-demo-slot-chain-spi/src/main/java/com/alibaba/csp/sentinel/demo/slot/DemoSlot.java Datei anzeigen

@@ -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


+ 2
- 1
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java Datei anzeigen

@@ -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
*/


+ 2
- 2
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java Datei anzeigen

@@ -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


+ 2
- 2
sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandCenterProvider.java Datei anzeigen

@@ -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");


+ 6
- 9
sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java Datei anzeigen

@@ -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();


+ 2
- 2
sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/heartbeat/HeartbeatSenderProvider.java Datei anzeigen

@@ -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 {


+ 2
- 2
sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/NettyHttpCommandCenter.java Datei anzeigen

@@ -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();


+ 2
- 2
sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/heartbeat/HttpHeartbeatSender.java Datei anzeigen

@@ -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;


Laden…
Abbrechen
Speichern