* 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
@@ -27,7 +27,7 @@ import com.alibaba.csp.sentinel.property.DynamicSentinelProperty; | |||||
import com.alibaba.csp.sentinel.property.PropertyListener; | import com.alibaba.csp.sentinel.property.PropertyListener; | ||||
import com.alibaba.csp.sentinel.property.SentinelProperty; | import com.alibaba.csp.sentinel.property.SentinelProperty; | ||||
import com.alibaba.csp.sentinel.util.AssertUtil; | 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; | import com.alibaba.csp.sentinel.util.StringUtil; | ||||
/** | /** | ||||
@@ -59,7 +59,7 @@ public final class GatewayApiDefinitionManager { | |||||
} | } | ||||
private static void initializeApiChangeObserverSpi() { | private static void initializeApiChangeObserverSpi() { | ||||
List<ApiDefinitionChangeObserver> listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class); | |||||
List<ApiDefinitionChangeObserver> listeners = SpiLoader.of(ApiDefinitionChangeObserver.class).loadInstanceList(); | |||||
for (ApiDefinitionChangeObserver e : listeners) { | for (ApiDefinitionChangeObserver e : listeners) { | ||||
API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e); | API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e); | ||||
RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {}" | 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.ParamFlowException; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | 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.slots.block.flow.param.ParameterMetricStorage; | ||||
import com.alibaba.csp.sentinel.spi.SpiOrder; | |||||
import com.alibaba.csp.sentinel.spi.Spi; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @since 1.6.1 | * @since 1.6.1 | ||||
*/ | */ | ||||
@SpiOrder(-4000) | |||||
@Spi(order = -4000) | |||||
public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -21,8 +21,9 @@ import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @since 1.6.1 | * @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. | * this class is reserved for compatibility with older versions. | ||||
* | |||||
* @see GatewayFlowSlot | * @see GatewayFlowSlot | ||||
* @see DefaultSlotChainBuilder | * @see DefaultSlotChainBuilder | ||||
*/ | */ | ||||
@@ -15,7 +15,7 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.cluster.client.codec; | 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.request.RequestEntityWriter; | ||||
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder; | import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
@@ -34,7 +34,7 @@ public final class ClientEntityCodecProvider { | |||||
} | } | ||||
private static void resolveInstance() { | private static void resolveInstance() { | ||||
RequestEntityWriter writer = SpiLoader.loadFirstInstance(RequestEntityWriter.class); | |||||
RequestEntityWriter writer = SpiLoader.of(RequestEntityWriter.class).loadFirstInstance(); | |||||
if (writer == null) { | if (writer == null) { | ||||
RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed"); | RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed"); | ||||
} else { | } else { | ||||
@@ -42,7 +42,7 @@ public final class ClientEntityCodecProvider { | |||||
RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}", | RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}", | ||||
requestEntityWriter.getClass().getCanonicalName()); | requestEntityWriter.getClass().getCanonicalName()); | ||||
} | } | ||||
ResponseEntityDecoder decoder = SpiLoader.loadFirstInstance(ResponseEntityDecoder.class); | |||||
ResponseEntityDecoder decoder = SpiLoader.of(ResponseEntityDecoder.class).loadFirstInstance(); | |||||
if (decoder == null) { | if (decoder == null) { | ||||
RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed"); | RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed"); | ||||
} else { | } 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.cluster.flow.rule.ClusterParamFlowRuleManager; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | ||||
import com.alibaba.csp.sentinel.spi.Spi; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
@@ -31,6 +32,7 @@ import java.util.Collection; | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @since 1.4.0 | * @since 1.4.0 | ||||
*/ | */ | ||||
@Spi(isDefault = true) | |||||
public class DefaultTokenService implements TokenService { | public class DefaultTokenService implements TokenService { | ||||
@Override | @Override | ||||
@@ -16,9 +16,8 @@ | |||||
package com.alibaba.csp.sentinel.cluster.server; | package com.alibaba.csp.sentinel.cluster.server; | ||||
import com.alibaba.csp.sentinel.cluster.TokenService; | 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.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.util.SpiLoader; | |||||
import com.alibaba.csp.sentinel.spi.SpiLoader; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
@@ -37,7 +36,7 @@ public final class TokenServiceProvider { | |||||
} | } | ||||
private static void resolveTokenServiceSpi() { | private static void resolveTokenServiceSpi() { | ||||
service = SpiLoader.loadFirstInstanceOrDefault(TokenService.class, DefaultTokenService.class); | |||||
service = SpiLoader.of(TokenService.class).loadFirstInstanceOrDefault(); | |||||
if (service != null) { | if (service != null) { | ||||
RecordLog.info("[TokenServiceProvider] Global token service resolved: " | RecordLog.info("[TokenServiceProvider] Global token service resolved: " | ||||
+ service.getClass().getCanonicalName()); | + 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.request.RequestEntityDecoder; | ||||
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityWriter; | import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityWriter; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.util.SpiLoader; | |||||
import com.alibaba.csp.sentinel.spi.SpiLoader; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
@@ -34,7 +34,7 @@ public final class ServerEntityCodecProvider { | |||||
} | } | ||||
private static void resolveInstance() { | private static void resolveInstance() { | ||||
ResponseEntityWriter writer = SpiLoader.loadFirstInstance(ResponseEntityWriter.class); | |||||
ResponseEntityWriter writer = SpiLoader.of(ResponseEntityWriter.class).loadFirstInstance(); | |||||
if (writer == null) { | if (writer == null) { | ||||
RecordLog.warn("[ServerEntityCodecProvider] No existing response entity writer, resolve failed"); | RecordLog.warn("[ServerEntityCodecProvider] No existing response entity writer, resolve failed"); | ||||
} else { | } else { | ||||
@@ -42,7 +42,7 @@ public final class ServerEntityCodecProvider { | |||||
RecordLog.info("[ServerEntityCodecProvider] Response entity writer resolved: {}", | RecordLog.info("[ServerEntityCodecProvider] Response entity writer resolved: {}", | ||||
responseEntityWriter.getClass().getCanonicalName()); | responseEntityWriter.getClass().getCanonicalName()); | ||||
} | } | ||||
RequestEntityDecoder decoder = SpiLoader.loadFirstInstance(RequestEntityDecoder.class); | |||||
RequestEntityDecoder decoder = SpiLoader.of(RequestEntityDecoder.class).loadFirstInstance(); | |||||
if (decoder == null) { | if (decoder == null) { | ||||
RecordLog.warn("[ServerEntityCodecProvider] No existing request entity decoder, resolve failed"); | RecordLog.warn("[ServerEntityCodecProvider] No existing request entity decoder, resolve failed"); | ||||
} else { | } else { | ||||
@@ -15,12 +15,12 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.cluster.server.processor; | package com.alibaba.csp.sentinel.cluster.server.processor; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.ServiceLoader; | |||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import com.alibaba.csp.sentinel.cluster.annotation.RequestType; | 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; | 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 Map<Integer, RequestProcessor> PROCESSOR_MAP = new ConcurrentHashMap<>(); | ||||
private static final ServiceLoader<RequestProcessor> SERVICE_LOADER = ServiceLoaderUtil.getServiceLoader( | |||||
RequestProcessor.class); | |||||
static { | static { | ||||
loadAndInit(); | loadAndInit(); | ||||
} | } | ||||
private static void 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); | Integer type = parseRequestType(processor); | ||||
if (type != null) { | if (type != null) { | ||||
PROCESSOR_MAP.put(type, processor); | PROCESSOR_MAP.put(type, processor); | ||||
@@ -70,5 +70,17 @@ public final class Constants { | |||||
*/ | */ | ||||
public static volatile boolean ON = true; | 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() {} | private Constants() {} | ||||
} | } |
@@ -16,7 +16,7 @@ | |||||
package com.alibaba.csp.sentinel.cluster.client; | package com.alibaba.csp.sentinel.cluster.client; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | 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. | * Provider for a universal {@link ClusterTokenClient} instance. | ||||
@@ -38,7 +38,7 @@ public final class TokenClientProvider { | |||||
} | } | ||||
private static void resolveTokenClientInstance() { | private static void resolveTokenClientInstance() { | ||||
ClusterTokenClient resolvedClient = SpiLoader.loadFirstInstance(ClusterTokenClient.class); | |||||
ClusterTokenClient resolvedClient = SpiLoader.of(ClusterTokenClient.class).loadFirstInstance(); | |||||
if (resolvedClient == null) { | if (resolvedClient == null) { | ||||
RecordLog.info( | RecordLog.info( | ||||
"[TokenClientProvider] No existing cluster token client, cluster client mode will not be activated"); | "[TokenClientProvider] No existing cluster token client, cluster client mode will not be activated"); | ||||
@@ -16,7 +16,7 @@ | |||||
package com.alibaba.csp.sentinel.cluster.server; | package com.alibaba.csp.sentinel.cluster.server; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.util.SpiLoader; | |||||
import com.alibaba.csp.sentinel.spi.SpiLoader; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
@@ -31,7 +31,7 @@ public final class EmbeddedClusterTokenServerProvider { | |||||
} | } | ||||
private static void resolveInstance() { | private static void resolveInstance() { | ||||
EmbeddedClusterTokenServer s = SpiLoader.loadFirstInstance(EmbeddedClusterTokenServer.class); | |||||
EmbeddedClusterTokenServer s = SpiLoader.of(EmbeddedClusterTokenServer.class).loadFirstInstance(); | |||||
if (s == null) { | if (s == null) { | ||||
RecordLog.warn("[EmbeddedClusterTokenServerProvider] No existing cluster token server, cluster server mode will not be activated"); | RecordLog.warn("[EmbeddedClusterTokenServerProvider] No existing cluster token server, cluster server mode will not be activated"); | ||||
} else { | } else { | ||||
@@ -41,6 +41,11 @@ public final class SentinelConfig { | |||||
*/ | */ | ||||
public static final int APP_TYPE_COMMON = 0; | 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 final Map<String, String> props = new ConcurrentHashMap<>(); | ||||
private static int appType = APP_TYPE_COMMON; | private static int appType = APP_TYPE_COMMON; | ||||
@@ -307,6 +312,15 @@ public final class SentinelConfig { | |||||
private static String toEnvKey(/*@NotBlank*/ String propKey) { | private static String toEnvKey(/*@NotBlank*/ String propKey) { | ||||
return propKey.toUpperCase().replace('.', '_'); | 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() {} | private SentinelConfig() {} | ||||
} | } |
@@ -21,7 +21,7 @@ import java.util.ServiceLoader; | |||||
import java.util.concurrent.atomic.AtomicBoolean; | import java.util.concurrent.atomic.AtomicBoolean; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | 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. | * Load registered init functions and execute in order. | ||||
@@ -43,9 +43,9 @@ public final class InitExecutor { | |||||
return; | return; | ||||
} | } | ||||
try { | try { | ||||
ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class); | |||||
List<InitFunc> initFuncs = SpiLoader.of(InitFunc.class).loadInstanceListSorted(); | |||||
List<OrderWrapper> initList = new ArrayList<OrderWrapper>(); | List<OrderWrapper> initList = new ArrayList<OrderWrapper>(); | ||||
for (InitFunc initFunc : loader) { | |||||
for (InitFunc initFunc : initFuncs) { | |||||
RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName()); | RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName()); | ||||
insertSorted(initList, initFunc); | insertSorted(initList, initFunc); | ||||
} | } | ||||
@@ -19,7 +19,7 @@ import java.util.ArrayList; | |||||
import java.util.List; | import java.util.List; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | 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. | * Get all {@link MetricExtension} via SPI. | ||||
@@ -35,7 +35,7 @@ public class MetricExtensionProvider { | |||||
} | } | ||||
private static void resolveInstance() { | private static void resolveInstance() { | ||||
List<MetricExtension> extensions = SpiLoader.loadInstanceList(MetricExtension.class); | |||||
List<MetricExtension> extensions = SpiLoader.of(MetricExtension.class).loadInstanceList(); | |||||
if (extensions.isEmpty()) { | if (extensions.isEmpty()) { | ||||
RecordLog.info("[MetricExtensionProvider] No existing MetricExtension found"); | 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.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; | 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. | * 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. | // Resolve the slot chain builder SPI. | ||||
slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class); | |||||
slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault(); | |||||
if (slotChainBuilder == null) { | if (slotChainBuilder == null) { | ||||
// Should not go through here. | // 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.ProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain; | import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain; | ||||
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder; | 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; | import java.util.List; | ||||
@@ -31,14 +32,14 @@ import java.util.List; | |||||
* @author qinan.qn | * @author qinan.qn | ||||
* @author leyou | * @author leyou | ||||
*/ | */ | ||||
@Spi(isDefault = true) | |||||
public class DefaultSlotChainBuilder implements SlotChainBuilder { | public class DefaultSlotChainBuilder implements SlotChainBuilder { | ||||
@Override | @Override | ||||
public ProcessorSlotChain build() { | public ProcessorSlotChain build() { | ||||
ProcessorSlotChain chain = new DefaultProcessorSlotChain(); | 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) { | for (ProcessorSlot slot : sortedSlotList) { | ||||
if (!(slot instanceof AbstractLinkedProcessorSlot)) { | if (!(slot instanceof AbstractLinkedProcessorSlot)) { | ||||
RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain"); | 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.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import com.alibaba.csp.sentinel.Constants; | |||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.node.DefaultNode; | import com.alibaba.csp.sentinel.node.DefaultNode; | ||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | 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. | * A {@link ProcessorSlot} that dedicates to {@link AuthorityRule} checking. | ||||
@@ -31,7 +32,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; | |||||
* @author leyou | * @author leyou | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@SpiOrder(-6000) | |||||
@Spi(order = Constants.ORDER_AUTHORITY_SLOT) | |||||
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -17,6 +17,7 @@ package com.alibaba.csp.sentinel.slots.block.degrade; | |||||
import java.util.List; | import java.util.List; | ||||
import com.alibaba.csp.sentinel.Constants; | |||||
import com.alibaba.csp.sentinel.Entry; | import com.alibaba.csp.sentinel.Entry; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.node.DefaultNode; | 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.slotchain.ResourceWrapper; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | import com.alibaba.csp.sentinel.slots.block.BlockException; | ||||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker; | 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. | * A {@link ProcessorSlot} dedicates to circuit breaking. | ||||
@@ -33,7 +34,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; | |||||
* @author Carpenter Lee | * @author Carpenter Lee | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@SpiOrder(-1000) | |||||
@Spi(order = Constants.ORDER_DEGRADE_SLOT) | |||||
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -15,12 +15,13 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.slots.block.flow; | 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.context.Context; | ||||
import com.alibaba.csp.sentinel.node.DefaultNode; | import com.alibaba.csp.sentinel.node.DefaultNode; | ||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | 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.AssertUtil; | ||||
import com.alibaba.csp.sentinel.util.function.Function; | import com.alibaba.csp.sentinel.util.function.Function; | ||||
@@ -137,7 +138,7 @@ import java.util.Map; | |||||
* @author jialiang.linjl | * @author jialiang.linjl | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@SpiOrder(-2000) | |||||
@Spi(order = Constants.ORDER_FLOW_SLOT) | |||||
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
private final FlowRuleChecker checker; | private final FlowRuleChecker checker; | ||||
@@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.slots.clusterbuilder; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import com.alibaba.csp.sentinel.Constants; | |||||
import com.alibaba.csp.sentinel.EntryType; | import com.alibaba.csp.sentinel.EntryType; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.context.ContextUtil; | 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.ProcessorSlotChain; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | ||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; | import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; | ||||
import com.alibaba.csp.sentinel.spi.SpiOrder; | |||||
import com.alibaba.csp.sentinel.spi.Spi; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
@@ -45,7 +46,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; | |||||
* | * | ||||
* @author jialiang.linjl | * @author jialiang.linjl | ||||
*/ | */ | ||||
@SpiOrder(-9000) | |||||
@Spi(isSingleton = false, order = Constants.ORDER_CLUSTER_BUILDER_SLOT) | |||||
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
/** | /** | ||||
@@ -15,19 +15,20 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.slots.logger; | 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.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.node.DefaultNode; | import com.alibaba.csp.sentinel.node.DefaultNode; | ||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | 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 | * A {@link com.alibaba.csp.sentinel.slotchain.ProcessorSlot} that is response for logging block exceptions | ||||
* to provide concrete logs for troubleshooting. | * to provide concrete logs for troubleshooting. | ||||
*/ | */ | ||||
@SpiOrder(-8000) | |||||
@Spi(order = Constants.ORDER_LOG_SLOT) | |||||
public class LogSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class LogSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -15,6 +15,7 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.slots.nodeselector; | 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.Context; | ||||
import com.alibaba.csp.sentinel.context.ContextUtil; | import com.alibaba.csp.sentinel.context.ContextUtil; | ||||
import com.alibaba.csp.sentinel.node.ClusterNode; | 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.node.EntranceNode; | ||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | 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.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
@@ -123,7 +124,7 @@ import java.util.Map; | |||||
* @see EntranceNode | * @see EntranceNode | ||||
* @see ContextUtil | * @see ContextUtil | ||||
*/ | */ | ||||
@SpiOrder(-10000) | |||||
@Spi(isSingleton = false, order = Constants.ORDER_NODE_SELECTOR_SLOT) | |||||
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> { | 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.ProcessorSlotEntryCallback; | ||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback; | import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.PriorityWaitException; | 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.util.TimeUtil; | ||||
import com.alibaba.csp.sentinel.Constants; | import com.alibaba.csp.sentinel.Constants; | ||||
import com.alibaba.csp.sentinel.EntryType; | import com.alibaba.csp.sentinel.EntryType; | ||||
@@ -48,7 +48,7 @@ import com.alibaba.csp.sentinel.slots.block.BlockException; | |||||
* @author jialiang.linjl | * @author jialiang.linjl | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@SpiOrder(-7000) | |||||
@Spi(order = Constants.ORDER_STATISTIC_SLOT) | |||||
public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -15,12 +15,13 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.slots.system; | 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.context.Context; | ||||
import com.alibaba.csp.sentinel.node.DefaultNode; | import com.alibaba.csp.sentinel.node.DefaultNode; | ||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | 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. | * A {@link ProcessorSlot} that dedicates to {@link SystemRule} checking. | ||||
@@ -28,7 +29,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; | |||||
* @author jialiang.linjl | * @author jialiang.linjl | ||||
* @author leyou | * @author leyou | ||||
*/ | */ | ||||
@SpiOrder(-5000) | |||||
@Spi(order = Constants.ORDER_SYSTEM_SLOT) | |||||
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @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.node.DefaultNode; | ||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | 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. | * An example slot that records current context and entry resource. | ||||
* | * | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@SpiOrder(-3500) | |||||
@Spi(order = -3500) | |||||
public class DemoSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class DemoSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -21,8 +21,9 @@ import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot; | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @since 0.2.0 | * @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. | * this class is reserved for compatibility with older versions. | ||||
* | |||||
* @see ParamFlowSlot | * @see ParamFlowSlot | ||||
* @see DefaultSlotChainBuilder | * @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.AbstractLinkedProcessorSlot; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | 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; | import java.util.List; | ||||
@@ -31,7 +31,7 @@ import java.util.List; | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @since 0.2.0 | * @since 0.2.0 | ||||
*/ | */ | ||||
@SpiOrder(-3000) | |||||
@Spi(order = -3000) | |||||
public class ParamFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | public class ParamFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | ||||
@Override | @Override | ||||
@@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.command; | |||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.transport.CommandCenter; | 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. | * Provider for a universal {@link CommandCenter} instance. | ||||
@@ -34,7 +34,7 @@ public final class CommandCenterProvider { | |||||
} | } | ||||
private static void resolveInstance() { | private static void resolveInstance() { | ||||
CommandCenter resolveCommandCenter = SpiLoader.loadHighestPriorityInstance(CommandCenter.class); | |||||
CommandCenter resolveCommandCenter = SpiLoader.of(CommandCenter.class).loadHighestPriorityInstance(); | |||||
if (resolveCommandCenter == null) { | if (resolveCommandCenter == null) { | ||||
RecordLog.warn("[CommandCenterProvider] WARN: No existing CommandCenter found"); | RecordLog.warn("[CommandCenterProvider] WARN: No existing CommandCenter found"); | ||||
@@ -15,13 +15,10 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.command; | 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.command.annotation.CommandMapping; | ||||
import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil; | |||||
import com.alibaba.csp.sentinel.spi.SpiLoader; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | import com.alibaba.csp.sentinel.util.StringUtil; | ||||
/** | /** | ||||
@@ -31,8 +28,7 @@ import com.alibaba.csp.sentinel.util.StringUtil; | |||||
*/ | */ | ||||
public class CommandHandlerProvider implements Iterable<CommandHandler> { | 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. | * 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() { | public Map<String, CommandHandler> namedHandlers() { | ||||
Map<String, CommandHandler> map = new HashMap<String, CommandHandler>(); | 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); | String name = parseCommandName(handler); | ||||
if (!StringUtil.isEmpty(name)) { | if (!StringUtil.isEmpty(name)) { | ||||
map.put(name, handler); | map.put(name, handler); | ||||
@@ -61,7 +58,7 @@ public class CommandHandlerProvider implements Iterable<CommandHandler> { | |||||
@Override | @Override | ||||
public Iterator<CommandHandler> iterator() { | public Iterator<CommandHandler> iterator() { | ||||
return serviceLoader.iterator(); | |||||
return spiLoader.loadInstanceList().iterator(); | |||||
} | } | ||||
private static final CommandHandlerProvider INSTANCE = new CommandHandlerProvider(); | 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.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.transport.HeartbeatSender; | import com.alibaba.csp.sentinel.transport.HeartbeatSender; | ||||
import com.alibaba.csp.sentinel.util.SpiLoader; | |||||
import com.alibaba.csp.sentinel.spi.SpiLoader; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
@@ -32,7 +32,7 @@ public final class HeartbeatSenderProvider { | |||||
} | } | ||||
private static void resolveInstance() { | private static void resolveInstance() { | ||||
HeartbeatSender resolved = SpiLoader.loadHighestPriorityInstance(HeartbeatSender.class); | |||||
HeartbeatSender resolved = SpiLoader.of(HeartbeatSender.class).loadHighestPriorityInstance(); | |||||
if (resolved == null) { | if (resolved == null) { | ||||
RecordLog.warn("[HeartbeatSenderProvider] WARN: No existing HeartbeatSender found"); | RecordLog.warn("[HeartbeatSenderProvider] WARN: No existing HeartbeatSender found"); | ||||
} else { | } else { | ||||
@@ -22,7 +22,7 @@ import java.util.concurrent.Executors; | |||||
import com.alibaba.csp.sentinel.command.CommandHandler; | import com.alibaba.csp.sentinel.command.CommandHandler; | ||||
import com.alibaba.csp.sentinel.command.CommandHandlerProvider; | import com.alibaba.csp.sentinel.command.CommandHandlerProvider; | ||||
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; | 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.transport.command.netty.HttpServer; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.transport.CommandCenter; | import com.alibaba.csp.sentinel.transport.CommandCenter; | ||||
@@ -32,7 +32,7 @@ import com.alibaba.csp.sentinel.transport.CommandCenter; | |||||
* | * | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@SpiOrder(SpiOrder.LOWEST_PRECEDENCE - 100) | |||||
@Spi(order = Spi.ORDER_LOWEST - 100) | |||||
public class NettyHttpCommandCenter implements CommandCenter { | public class NettyHttpCommandCenter implements CommandCenter { | ||||
private final HttpServer server = new HttpServer(); | 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.Constants; | ||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | import com.alibaba.csp.sentinel.config.SentinelConfig; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | 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.HeartbeatSender; | ||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig; | import com.alibaba.csp.sentinel.transport.config.TransportConfig; | ||||
import com.alibaba.csp.sentinel.transport.endpoint.Protocol; | import com.alibaba.csp.sentinel.transport.endpoint.Protocol; | ||||
@@ -42,7 +42,7 @@ import java.util.List; | |||||
* @author Carpenter Lee | * @author Carpenter Lee | ||||
* @author Leo Li | * @author Leo Li | ||||
*/ | */ | ||||
@SpiOrder(SpiOrder.LOWEST_PRECEDENCE - 100) | |||||
@Spi(order = Spi.ORDER_LOWEST - 100) | |||||
public class HttpHeartbeatSender implements HeartbeatSender { | public class HttpHeartbeatSender implements HeartbeatSender { | ||||
private final CloseableHttpClient client; | private final CloseableHttpClient client; | ||||