Browse Source

Add annotation extension for Java EE CDI (#1541)

master
seasidesky GitHub 4 years ago
parent
commit
7b53b61373
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1281 additions and 0 deletions
  1. +5
    -0
      pom.xml
  2. +1
    -0
      sentinel-extension/pom.xml
  3. +60
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/README.md
  4. +59
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/pom.xml
  5. +332
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/AbstractSentinelInterceptorSupport.java
  6. +51
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/MethodWrapper.java
  7. +85
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/ResourceMetadataRegistry.java
  8. +117
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/SentinelResourceBinding.java
  9. +73
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/SentinelResourceInterceptor.java
  10. +5
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/resources/META-INF/beans.xml
  11. +39
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/AbstractSentinelInterceptorSupportTest.java
  12. +44
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/MethodWrapperTest.java
  13. +86
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/ResourceMetadataRegistryTest.java
  14. +195
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/integration/SentinelAnnotationInterceptorIntegrationTest.java
  15. +84
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/integration/service/FooService.java
  16. +37
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/integration/service/FooUtil.java
  17. +8
    -0
      sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/resources/META-INF/beans.xml

+ 5
- 0
pom.xml View File

@@ -99,6 +99,11 @@
<artifactId>sentinel-annotation-aspectj</artifactId> <artifactId>sentinel-annotation-aspectj</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-cdi-interceptor</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.alibaba.csp</groupId> <groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId> <artifactId>sentinel-parameter-flow-control</artifactId>


+ 1
- 0
sentinel-extension/pom.xml View File

@@ -22,6 +22,7 @@
<module>sentinel-datasource-spring-cloud-config</module> <module>sentinel-datasource-spring-cloud-config</module>
<module>sentinel-datasource-consul</module> <module>sentinel-datasource-consul</module>
<module>sentinel-datasource-etcd</module> <module>sentinel-datasource-etcd</module>
<module>sentinel-annotation-cdi-interceptor</module>
</modules> </modules>


</project> </project>

+ 60
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/README.md View File

@@ -0,0 +1,60 @@
# Sentinel Annotation cdi interceptor

This extension is an implementation using cdi interceptor for Sentinel annotations. [JSR 318: Enterprise JavaBeansTM 3.1/Interceptors 1.2](https://jcp.org/en/jsr/detail?id=318) define the javax interceptor and [CDI](http://www.cdi-spec.org/) related specifications extends the Java Interceptors specification and allows interceptor bindings to be applied to CDI stereotypes.

[CDI](http://www.cdi-spec.org/) is an abbreviation for Contexts and Dependency Injection, the related JSRs are : [JSR 365: Contexts and Dependency Injection for JavaTM 2.0](https://jcp.org/en/jsr/detail?id=365), [JSR 346: Contexts and Dependency Injection for JavaTM EE 1.1](https://jcp.org/en/jsr/detail?id=346), [JSR 299: Contexts and Dependency Injection for the JavaTM EE platform](https://jcp.org/en/jsr/detail?id=299)

## Annotation

The `@SentinelResourceBinding` is modified from `@SentinelResource` by adding `@InterceptorBinding` for CDI specification, and in order to interceptor all kinds of `@SentinelResourceBinding` with different attributes in one interceptor, `@SentinelResourceBinding`'s all attribute is annotated by `@Nonbinding`

The `@SentinelResource` annotation indicates a resource definition, including:

- `value`: Resource name, required (cannot be empty)
- `entryType`: Resource entry type (inbound or outbound), `EntryType.OUT` by default
- `fallback` (refactored since 1.6.0): Fallback method when exceptions caught (including `BlockException`, but except the exceptions defined in `exceptionsToIgnore`). The fallback method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `fallbackClass` with corresponding `Class` (Note the method in other classes must be *static*). The method signature requirement:
- The return type should match the origin method;
- The parameter list should match the origin method, and an additional `Throwable` parameter can be provided to get the actual exception.
- `defaultFallback` (since 1.6.0): The default fallback method when exceptions caught (including `BlockException`, but except the exceptions defined in `exceptionsToIgnore`). Its intended to be a universal common fallback method. The method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `fallbackClass` with corresponding `Class` (Note the method in other classes must be *static*). The default fallback method signature requirement:
- The return type should match the origin method;
- parameter list should be empty, and an additional `Throwable` parameter can be provided to get the actual exception.
- `blockHandler`: Handler method that handles `BlockException` when blocked. The parameter list of the method should match original method, with the last additional parameter type `BlockException`. The return type should be same as the original method. The `blockHandler` method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `blockHandlerClass` with corresponding `Class` (Note the method in other classes must be *static*).
- `exceptionsToIgnore` (since 1.6.0): List of business exception classes that should not be traced and caught in fallback.
- `exceptionsToTrace` (since 1.5.1): List of business exception classes to trace and record. In most cases, using `exceptionsToIgnore` is better. If both `exceptionsToTrace` and `exceptionsToIgnore` are present, only `exceptionsToIgnore` will be activated.

For example:

```java
@SentinelResourceBinding(value = "abc", fallback = "doFallback")
public String doSomething(long i) {
return "Hello " + i;
}

public String doFallback(long i, Throwable t) {
// Return fallback value.
return "fallback";
}

public String defaultFallback(Throwable t) {
return "default_fallback";
}
```

## Configuration

according to [9.4. Interceptor enablement and ordering](https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#enabled_interceptors) to enable interceptor, it should be configured in resources/META-INF/beans.xml :

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
<interceptors>
<class>com.alibaba.csp.sentinel.annotation.cdi.interceptor.SentinelResourceInterceptor</class>
</interceptors>
</beans>
```





+ 59
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/pom.xml View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-extension</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.8.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>sentinel-annotation-cdi-interceptor</artifactId>
<packaging>jar</packaging>

<properties>
<jakarta.interceptor-api.version>1.2.5</jakarta.interceptor-api.version>
<jakarta.enterprise.cdi-api.version>2.0.2</jakarta.enterprise.cdi-api.version>
</properties>

<dependencies>
<dependency>
<groupId>jakarta.interceptor</groupId>
<artifactId>jakarta.interceptor-api</artifactId>
<version>${jakarta.interceptor-api.version}</version>
</dependency>

<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>${jakarta.enterprise.cdi-api.version}</version>
</dependency>

<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-shaded</artifactId>
<version>3.1.4.Final</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>

+ 332
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/AbstractSentinelInterceptorSupport.java View File

@@ -0,0 +1,332 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.MethodUtil;
import com.alibaba.csp.sentinel.util.StringUtil;

import javax.interceptor.InvocationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
* Some common functions for Sentinel annotation aspect.
*
* @author Eric Zhao
*/
public abstract class AbstractSentinelInterceptorSupport {

protected void traceException(Throwable ex) {
Tracer.trace(ex);
}

protected void traceException(Throwable ex, SentinelResourceBinding annotation) {
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
// The ignore list will be checked first.
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
return;
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
}
}

/**
* Check whether the exception is in provided list of exception classes.
*
* @param ex provided throwable
* @param exceptions list of exceptions
* @return true if it is in the list, otherwise false
*/
protected boolean exceptionBelongsTo(Throwable ex, Class<? extends Throwable>[] exceptions) {
if (exceptions == null) {
return false;
}
for (Class<? extends Throwable> exceptionClass : exceptions) {
if (exceptionClass.isAssignableFrom(ex.getClass())) {
return true;
}
}
return false;
}

protected String getResourceName(String resourceName, /*@NonNull*/ Method method) {
// If resource name is present in annotation, use this value.
if (StringUtil.isNotBlank(resourceName)) {
return resourceName;
}
// Parse name of target method.
return MethodUtil.resolveMethodName(method);
}

protected Object handleFallback(InvocationContext ctx, SentinelResourceBinding annotation, Throwable ex)
throws Throwable {
return handleFallback(ctx, annotation.fallback(), annotation.defaultFallback(), annotation.fallbackClass(), ex);
}

protected Object handleFallback(InvocationContext ctx, String fallback, String defaultFallback,
Class<?>[] fallbackClass, Throwable ex) throws Throwable {
Object[] originArgs = ctx.getParameters();

// Execute fallback function if configured.
Method fallbackMethod = extractFallbackMethod(ctx, fallback, fallbackClass);
if (fallbackMethod != null) {
// Construct args.
int paramCount = fallbackMethod.getParameterTypes().length;
Object[] args;
if (paramCount == originArgs.length) {
args = originArgs;
} else {
args = Arrays.copyOf(originArgs, originArgs.length + 1);
args[args.length - 1] = ex;
}

try {
if (isStatic(fallbackMethod)) {
return fallbackMethod.invoke(null, args);
}
return fallbackMethod.invoke(ctx.getTarget(), args);
} catch (InvocationTargetException e) {
// throw the actual exception
throw e.getTargetException();
}
}
// If fallback is absent, we'll try the defaultFallback if provided.
return handleDefaultFallback(ctx, defaultFallback, fallbackClass, ex);
}

protected Object handleDefaultFallback(InvocationContext ctx, String defaultFallback,
Class<?>[] fallbackClass, Throwable ex) throws Throwable {
// Execute the default fallback function if configured.
Method fallbackMethod = extractDefaultFallbackMethod(ctx, defaultFallback, fallbackClass);
if (fallbackMethod != null) {
// Construct args.
Object[] args = fallbackMethod.getParameterTypes().length == 0 ? new Object[0] : new Object[] {ex};
try {
if (isStatic(fallbackMethod)) {
return fallbackMethod.invoke(null, args);
}
return fallbackMethod.invoke(ctx.getTarget(), args);
} catch (InvocationTargetException e) {
// throw the actual exception
throw e.getTargetException();
}
}

// If no any fallback is present, then directly throw the exception.
throw ex;
}

protected Object handleBlockException(InvocationContext ctx, SentinelResourceBinding annotation, BlockException ex)
throws Throwable {

// Execute block handler if configured.
Method blockHandlerMethod = extractBlockHandlerMethod(ctx, annotation.blockHandler(),
annotation.blockHandlerClass());
if (blockHandlerMethod != null) {
Object[] originArgs = ctx.getParameters();
// Construct args.
Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);
args[args.length - 1] = ex;
try {
if (isStatic(blockHandlerMethod)) {
return blockHandlerMethod.invoke(null, args);
}
return blockHandlerMethod.invoke(ctx.getTarget(), args);
} catch (InvocationTargetException e) {
// throw the actual exception
throw e.getTargetException();
}
}

// If no block handler is present, then go to fallback.
return handleFallback(ctx, annotation, ex);
}

private Method extractFallbackMethod(InvocationContext ctx, String fallbackName, Class<?>[] locationClass) {
if (StringUtil.isBlank(fallbackName)) {
return null;
}
boolean mustStatic = locationClass != null && locationClass.length >= 1;
Class<?> clazz = mustStatic ? locationClass[0] : ctx.getTarget().getClass();
MethodWrapper m = ResourceMetadataRegistry.lookupFallback(clazz, fallbackName);
if (m == null) {
// First time, resolve the fallback.
Method method = resolveFallbackInternal(ctx, fallbackName, clazz, mustStatic);
// Cache the method instance.
ResourceMetadataRegistry.updateFallbackFor(clazz, fallbackName, method);
return method;
}
if (!m.isPresent()) {
return null;
}
return m.getMethod();
}

private Method extractDefaultFallbackMethod(InvocationContext ctx, String defaultFallback,
Class<?>[] locationClass) {
if (StringUtil.isBlank(defaultFallback)) {
return null;
}
boolean mustStatic = locationClass != null && locationClass.length >= 1;
Class<?> clazz = mustStatic ? locationClass[0] : ctx.getTarget().getClass();

MethodWrapper m = ResourceMetadataRegistry.lookupDefaultFallback(clazz, defaultFallback);
if (m == null) {
// First time, resolve the default fallback.
Class<?> originReturnType = resolveMethod(ctx).getReturnType();
// Default fallback allows two kinds of parameter list.
// One is empty parameter list.
Class<?>[] defaultParamTypes = new Class<?>[0];
// The other is a single parameter {@link Throwable} to get relevant exception info.
Class<?>[] paramTypeWithException = new Class<?>[] {Throwable.class};
// We first find the default fallback with empty parameter list.
Method method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, defaultParamTypes);
// If default fallback with empty params is absent, we then try to find the other one.
if (method == null) {
method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, paramTypeWithException);
}
// Cache the method instance.
ResourceMetadataRegistry.updateDefaultFallbackFor(clazz, defaultFallback, method);
return method;
}
if (!m.isPresent()) {
return null;
}
return m.getMethod();
}

private Method resolveFallbackInternal(InvocationContext ctx, /*@NonNull*/ String name, Class<?> clazz,
boolean mustStatic) {
Method originMethod = resolveMethod(ctx);
// Fallback function allows two kinds of parameter list.
Class<?>[] defaultParamTypes = originMethod.getParameterTypes();
Class<?>[] paramTypesWithException = Arrays.copyOf(defaultParamTypes, defaultParamTypes.length + 1);
paramTypesWithException[paramTypesWithException.length - 1] = Throwable.class;
// We first find the fallback matching the signature of origin method.
Method method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(), defaultParamTypes);
// If fallback matching the origin method is absent, we then try to find the other one.
if (method == null) {
method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(), paramTypesWithException);
}
return method;
}

private Method extractBlockHandlerMethod(InvocationContext ctx, String name, Class<?>[] locationClass) {
if (StringUtil.isBlank(name)) {
return null;
}

boolean mustStatic = locationClass != null && locationClass.length >= 1;
Class<?> clazz;
if (mustStatic) {
clazz = locationClass[0];
} else {
// By default current class.
clazz = ctx.getTarget().getClass();
}
MethodWrapper m = ResourceMetadataRegistry.lookupBlockHandler(clazz, name);
if (m == null) {
// First time, resolve the block handler.
Method method = resolveBlockHandlerInternal(ctx, name, clazz, mustStatic);
// Cache the method instance.
ResourceMetadataRegistry.updateBlockHandlerFor(clazz, name, method);
return method;
}
if (!m.isPresent()) {
return null;
}
return m.getMethod();
}

private Method resolveBlockHandlerInternal(InvocationContext ctx, /*@NonNull*/ String name, Class<?> clazz,
boolean mustStatic) {
Method originMethod = resolveMethod(ctx);
Class<?>[] originList = originMethod.getParameterTypes();
Class<?>[] parameterTypes = Arrays.copyOf(originList, originList.length + 1);
parameterTypes[parameterTypes.length - 1] = BlockException.class;
return findMethod(mustStatic, clazz, name, originMethod.getReturnType(), parameterTypes);
}

private boolean checkStatic(boolean mustStatic, Method method) {
return !mustStatic || isStatic(method);
}

private Method findMethod(boolean mustStatic, Class<?> clazz, String name, Class<?> returnType,
Class<?>... parameterTypes) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (name.equals(method.getName()) && checkStatic(mustStatic, method)
&& returnType.isAssignableFrom(method.getReturnType())
&& Arrays.equals(parameterTypes, method.getParameterTypes())) {

RecordLog.info("Resolved method [{}] in class [{}]", name, clazz.getCanonicalName());
return method;
}
}
// Current class not found, find in the super classes recursively.
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass)) {
return findMethod(mustStatic, superClass, name, returnType, parameterTypes);
} else {
String methodType = mustStatic ? " static" : "";
RecordLog.warn("Cannot find{} method [{}] in class [{}] with parameters {}",
methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes));
return null;
}
}

private boolean isStatic(Method method) {
return Modifier.isStatic(method.getModifiers());
}

protected Method resolveMethod(InvocationContext ctx) {
Class<?> targetClass = ctx.getTarget().getClass();

Method method = getDeclaredMethodFor(targetClass, ctx.getMethod().getName(),
ctx.getMethod().getParameterTypes());
if (method == null) {
throw new IllegalStateException("Cannot resolve target method: " + ctx.getMethod().getName());
}
return method;
}

/**
* Get declared method with provided name and parameterTypes in given class and its super classes.
* All parameters should be valid.
*
* @param clazz class where the method is located
* @param name method name
* @param parameterTypes method parameter type list
* @return resolved method, null if not found
*/
private Method getDeclaredMethodFor(Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
return clazz.getDeclaredMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
return getDeclaredMethodFor(superClass, name, parameterTypes);
}
}
return null;
}
}

+ 51
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/MethodWrapper.java View File

@@ -0,0 +1,51 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import java.lang.reflect.Method;

/**
* @author Eric Zhao
*/
class MethodWrapper {

private final Method method;
private final boolean present;

private MethodWrapper(Method method, boolean present) {
this.method = method;
this.present = present;
}

static MethodWrapper wrap(Method method) {
if (method == null) {
return none();
}
return new MethodWrapper(method, true);
}

static MethodWrapper none() {
return new MethodWrapper(null, false);
}

Method getMethod() {
return method;
}

boolean isPresent() {
return present;
}
}

+ 85
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/ResourceMetadataRegistry.java View File

@@ -0,0 +1,85 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import com.alibaba.csp.sentinel.util.StringUtil;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Registry for resource configuration metadata (e.g. fallback method)
*
* @author Eric Zhao
*/
final class ResourceMetadataRegistry {

private static final Map<String, MethodWrapper> FALLBACK_MAP = new ConcurrentHashMap<>();
private static final Map<String, MethodWrapper> DEFAULT_FALLBACK_MAP = new ConcurrentHashMap<>();
private static final Map<String, MethodWrapper> BLOCK_HANDLER_MAP = new ConcurrentHashMap<>();

static MethodWrapper lookupFallback(Class<?> clazz, String name) {
return FALLBACK_MAP.get(getKey(clazz, name));
}

static MethodWrapper lookupDefaultFallback(Class<?> clazz, String name) {
return DEFAULT_FALLBACK_MAP.get(getKey(clazz, name));
}

static MethodWrapper lookupBlockHandler(Class<?> clazz, String name) {
return BLOCK_HANDLER_MAP.get(getKey(clazz, name));
}

static void updateFallbackFor(Class<?> clazz, String name, Method method) {
if (clazz == null || StringUtil.isBlank(name)) {
throw new IllegalArgumentException("Bad argument");
}
FALLBACK_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method));
}

static void updateDefaultFallbackFor(Class<?> clazz, String name, Method method) {
if (clazz == null || StringUtil.isBlank(name)) {
throw new IllegalArgumentException("Bad argument");
}
DEFAULT_FALLBACK_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method));
}

static void updateBlockHandlerFor(Class<?> clazz, String name, Method method) {
if (clazz == null || StringUtil.isBlank(name)) {
throw new IllegalArgumentException("Bad argument");
}
BLOCK_HANDLER_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method));
}

private static String getKey(Class<?> clazz, String name) {
return String.format("%s:%s", clazz.getCanonicalName(), name);
}

/**
* Only for internal test.
*/
static void clearFallbackMap() {
FALLBACK_MAP.clear();
}

/**
* Only for internal test.
*/
static void clearBlockHandlerMap() {
BLOCK_HANDLER_MAP.clear();
}
}

+ 117
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/SentinelResourceBinding.java View File

@@ -0,0 +1,117 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import com.alibaba.csp.sentinel.EntryType;

import javax.enterprise.util.Nonbinding;
import javax.interceptor.InterceptorBinding;
import java.lang.annotation.*;

/**
* The annotation indicates a definition of Sentinel resource.
*
* @author Eric Zhao
*/
@InterceptorBinding
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResourceBinding {

/**
* @return name of the Sentinel resource
*/
@Nonbinding
String value() default "";

/**
* @return the entry type (inbound or outbound), outbound by default
*/
@Nonbinding
EntryType entryType() default EntryType.OUT;

/**
* @return the classification (type) of the resource
* @since 1.7.0
*/
@Nonbinding
int resourceType() default 0;

/**
* @return name of the block exception function, empty by default
*/
@Nonbinding
String blockHandler() default "";

/**
* The {@code blockHandler} is located in the same class with the original method by default.
* However, if some methods share the same signature and intend to set the same block handler,
* then users can set the class where the block handler exists. Note that the block handler method
* must be static.
*
* @return the class where the block handler exists, should not provide more than one classes
*/
@Nonbinding
Class<?>[] blockHandlerClass() default {};

/**
* @return name of the fallback function, empty by default
*/
@Nonbinding
String fallback() default "";

/**
* The {@code defaultFallback} is used as the default universal fallback method.
* It should not accept any parameters, and the return type should be compatible
* with the original method.
*
* @return name of the default fallback method, empty by default
* @since 1.6.0
*/
@Nonbinding
String defaultFallback() default "";

/**
* The {@code fallback} is located in the same class with the original method by default.
* However, if some methods share the same signature and intend to set the same fallback,
* then users can set the class where the fallback function exists. Note that the shared fallback method
* must be static.
*
* @return the class where the fallback method is located (only single class)
* @since 1.6.0
*/
@Nonbinding
Class<?>[] fallbackClass() default {};

/**
* @return the list of exception classes to trace, {@link Throwable} by default
* @since 1.5.1
*/
@Nonbinding
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};

/**
* Indicates the exceptions to be ignored. Note that {@code exceptionsToTrace} should
* not appear with {@code exceptionsToIgnore} at the same time, or {@code exceptionsToIgnore}
* will be of higher precedence.
*
* @return the list of exception classes to ignore, empty by default
* @since 1.6.0
*/
@Nonbinding
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}

+ 73
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/SentinelResourceInterceptor.java View File

@@ -0,0 +1,73 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;

import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

/**
* @author sea
*/
@Interceptor
@SentinelResourceBinding
@Priority(0)
public class SentinelResourceInterceptor extends AbstractSentinelInterceptorSupport {

@AroundInvoke
Object aroundInvoke(InvocationContext ctx) throws Throwable {
SentinelResourceBinding annotation = ctx.getMethod().getAnnotation(SentinelResourceBinding.class);
if (annotation == null) {
// Should not go through here.
throw new IllegalStateException("Wrong state for SentinelResource annotation");
}

String resourceName = getResourceName(annotation.value(), ctx.getMethod());
EntryType entryType = annotation.entryType();
int resourceType = annotation.resourceType();
Entry entry = null;
try {
entry = SphU.entry(resourceName, resourceType, entryType, ctx.getParameters());
Object result = ctx.proceed();
return result;
} catch (BlockException ex) {
return handleBlockException(ctx, annotation, ex);
} catch (Throwable ex) {
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
// The ignore list will be checked first.
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(ctx, annotation, ex);
}

// No fallback function can handle the exception, so throw it out.
throw ex;
} finally {
if (entry != null) {
entry.exit(1, ctx.getParameters());
}
}
}
}

+ 5
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/main/resources/META-INF/beans.xml View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
</beans>

+ 39
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/AbstractSentinelInterceptorSupportTest.java View File

@@ -0,0 +1,39 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import com.alibaba.csp.sentinel.annotation.cdi.interceptor.integration.service.FooService;
import org.junit.Test;

import java.lang.reflect.Method;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author sea
*/
public class AbstractSentinelInterceptorSupportTest extends AbstractSentinelInterceptorSupport {

@Test
public void testGetResourceName() throws Exception {
Method method = FooService.class.getMethod("random");
String resourceName = "someRandom";
String expectedResolvedName = FooService.class.getName() + ":random()";
assertThat(getResourceName(resourceName, method)).isEqualTo(resourceName);
assertThat(getResourceName(null, method)).isEqualTo(expectedResolvedName);
assertThat(getResourceName("", method)).isEqualTo(expectedResolvedName);
}
}

+ 44
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/MethodWrapperTest.java View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import org.junit.Test;

import java.lang.reflect.Method;

import static org.assertj.core.api.Assertions.assertThat;


/**
* @author Eric Zhao
*/
public class MethodWrapperTest {

@Test
public void testWrapMethod() {
Method method = String.class.getMethods()[0];
MethodWrapper m = MethodWrapper.wrap(method);
assertThat(m.isPresent()).isTrue();
assertThat(m.getMethod()).isSameAs(method);
}

@Test
public void testNone() {
MethodWrapper none = MethodWrapper.none();
assertThat(none.isPresent()).isFalse();
assertThat(none.getMethod()).isNull();
}
}

+ 86
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/ResourceMetadataRegistryTest.java View File

@@ -0,0 +1,86 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor;

import com.alibaba.csp.sentinel.annotation.cdi.interceptor.integration.service.FooService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;

import static org.assertj.core.api.Assertions.assertThat;


/**
* @author Eric Zhao
*/
public class ResourceMetadataRegistryTest {

@Before
public void setUp() throws Exception {
ResourceMetadataRegistry.clearBlockHandlerMap();
ResourceMetadataRegistry.clearFallbackMap();
}

@After
public void tearDown() throws Exception {
ResourceMetadataRegistry.clearBlockHandlerMap();
ResourceMetadataRegistry.clearFallbackMap();
}

@Test
public void testUpdateThenLookupFallback() {
Class<?> clazz = FooService.class;
String methodName = "someMethodFallback";
Method method = clazz.getMethods()[0];
assertThat(ResourceMetadataRegistry.lookupFallback(clazz, methodName)).isNull();

ResourceMetadataRegistry.updateFallbackFor(clazz, methodName, null);
assertThat(ResourceMetadataRegistry.lookupFallback(clazz, methodName).isPresent()).isFalse();

ResourceMetadataRegistry.updateFallbackFor(clazz, methodName, method);
MethodWrapper wrapper = ResourceMetadataRegistry.lookupFallback(clazz, methodName);
assertThat(wrapper.isPresent()).isTrue();
assertThat(wrapper.getMethod()).isSameAs(method);
}

@Test
public void testUpdateThenLookupBlockHandler() {
Class<?> clazz = FooService.class;
String methodName = "someMethodBlockHand;er";
Method method = clazz.getMethods()[1];
assertThat(ResourceMetadataRegistry.lookupBlockHandler(clazz, methodName)).isNull();

ResourceMetadataRegistry.updateBlockHandlerFor(clazz, methodName, null);
assertThat(ResourceMetadataRegistry.lookupBlockHandler(clazz, methodName).isPresent()).isFalse();

ResourceMetadataRegistry.updateBlockHandlerFor(clazz, methodName, method);
MethodWrapper wrapper = ResourceMetadataRegistry.lookupBlockHandler(clazz, methodName);
assertThat(wrapper.isPresent()).isTrue();
assertThat(wrapper.getMethod()).isSameAs(method);
}

@Test(expected = IllegalArgumentException.class)
public void testUpdateBlockHandlerBadArgument() {
ResourceMetadataRegistry.updateBlockHandlerFor(null, "sxs", String.class.getMethods()[0]);
}

@Test(expected = IllegalArgumentException.class)
public void testUpdateFallbackBadArgument() {
ResourceMetadataRegistry.updateBlockHandlerFor(String.class, "", String.class.getMethods()[0]);
}
}

+ 195
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/integration/SentinelAnnotationInterceptorIntegrationTest.java View File

@@ -0,0 +1,195 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor.integration;

import com.alibaba.csp.sentinel.annotation.cdi.interceptor.integration.service.FooService;
import com.alibaba.csp.sentinel.annotation.cdi.interceptor.integration.service.FooUtil;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
import com.alibaba.csp.sentinel.util.MethodUtil;
import org.junit.*;

import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import java.util.ArrayList;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

/**
* @author sea
*/
public class SentinelAnnotationInterceptorIntegrationTest {

static SeContainer container;

FooService fooService;

@BeforeClass
public static void init() {
SeContainerInitializer containerInit = SeContainerInitializer.newInstance();
container = containerInit.initialize();
}

@AfterClass
public static void shutdown() {
container.close();
}

@Before
public void setUp() throws Exception {
FlowRuleManager.loadRules(new ArrayList<FlowRule>());
ClusterBuilderSlot.resetClusterNodes();
fooService = container.select(FooService.class).get();
}

@After
public void tearDown() throws Exception {
FlowRuleManager.loadRules(new ArrayList<FlowRule>());
ClusterBuilderSlot.resetClusterNodes();
}

@Test
public void testForeignBlockHandlerClass() throws Exception {
assertThat(fooService.random()).isNotEqualTo(FooUtil.BLOCK_FLAG);
String resourceName = MethodUtil.resolveMethodName(FooService.class.getDeclaredMethod("random"));
ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();

FlowRuleManager.loadRules(Collections.singletonList(
new FlowRule(resourceName).setCount(0)
));
assertThat(fooService.random()).isEqualTo(FooUtil.BLOCK_FLAG);
assertThat(cn.blockQps()).isPositive();
}

@Test(expected = FlowException.class)
public void testBlockHandlerNotFound() {
assertThat(fooService.baz("Sentinel")).isEqualTo("cheers, Sentinel");
String resourceName = "apiBaz";
ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();

FlowRuleManager.loadRules(Collections.singletonList(
new FlowRule(resourceName).setCount(0)
));
fooService.baz("Sentinel");
}

@Test
public void testAnnotationExceptionsToIgnore() {
assertThat(fooService.baz("Sentinel")).isEqualTo("cheers, Sentinel");
String resourceName = "apiBaz";
ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();

try {
fooService.baz("fail");
fail("should not reach here");
} catch (IllegalMonitorStateException ex) {
assertThat(cn.exceptionQps()).isZero();
}
}

@Test
public void testFallbackWithNoParams() throws Exception {
assertThat(fooService.fooWithFallback(1)).isEqualTo("Hello for 1");
String resourceName = "apiFooWithFallback";
ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();

// Fallback should be ignored for this.
try {
fooService.fooWithFallback(5758);
fail("should not reach here");
} catch (IllegalAccessException e) {
assertThat(cn.exceptionQps()).isZero();
}

// Fallback should take effect.
assertThat(fooService.fooWithFallback(5763)).isEqualTo("eee...");
assertThat(cn.exceptionQps()).isPositive();
assertThat(cn.blockQps()).isZero();

FlowRuleManager.loadRules(Collections.singletonList(
new FlowRule(resourceName).setCount(0)
));
// Fallback should not take effect for BlockException, as blockHandler is configured.
assertThat(fooService.fooWithFallback(2221)).isEqualTo("Oops, 2221");
assertThat(cn.blockQps()).isPositive();
}

@Test
public void testDefaultFallbackWithSingleParam() {
assertThat(fooService.anotherFoo(1)).isEqualTo("Hello for 1");
String resourceName = "apiAnotherFooWithDefaultFallback";
ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();

// Default fallback should take effect.
assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT);
assertThat(cn.exceptionQps()).isPositive();
assertThat(cn.blockQps()).isZero();

FlowRuleManager.loadRules(Collections.singletonList(
new FlowRule(resourceName).setCount(0)
));
// Default fallback should also take effect for BlockException.
assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT);
assertThat(cn.blockQps()).isPositive();
}

@Test
public void testNormalBlockHandlerAndFallback() throws Exception {
assertThat(fooService.foo(1)).isEqualTo("Hello for 1");
String resourceName = "apiFoo";
ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();

// Test for biz exception.
try {
fooService.foo(5758);
fail("should not reach here");
} catch (Exception ex) {
// Should not be traced.
assertThat(cn.exceptionQps()).isZero();
}

try {
fooService.foo(5763);
fail("should not reach here");
} catch (Exception ex) {
assertThat(cn.exceptionQps()).isPositive();
}

// Test for blockHandler
FlowRuleManager.loadRules(Collections.singletonList(
new FlowRule(resourceName).setCount(0)
));
assertThat(fooService.foo(1121)).isEqualTo("Oops, 1121");
assertThat(cn.blockQps()).isPositive();
}
}

+ 84
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/integration/service/FooService.java View File

@@ -0,0 +1,84 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor.integration.service;
import com.alibaba.csp.sentinel.annotation.cdi.interceptor.SentinelResourceBinding;
import com.alibaba.csp.sentinel.slots.block.BlockException;

import javax.enterprise.context.ApplicationScoped;
import java.util.concurrent.ThreadLocalRandom;

/**
* @author Eric Zhao
* @author sea
*/
@ApplicationScoped
public class FooService {

@SentinelResourceBinding(value = "apiFoo", blockHandler = "fooBlockHandler",
exceptionsToTrace = {IllegalArgumentException.class})
public String foo(int i) throws Exception {
if (i == 5758) {
throw new IllegalAccessException();
}
if (i == 5763) {
throw new IllegalArgumentException();
}
return "Hello for " + i;
}

@SentinelResourceBinding(value = "apiFooWithFallback", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc",
exceptionsToTrace = {IllegalArgumentException.class})
public String fooWithFallback(int i) throws Exception {
if (i == 5758) {
throw new IllegalAccessException();
}
if (i == 5763) {
throw new IllegalArgumentException();
}
return "Hello for " + i;
}

@SentinelResourceBinding(value = "apiAnotherFooWithDefaultFallback", defaultFallback = "globalDefaultFallback",
fallbackClass = {FooUtil.class})
public String anotherFoo(int i) {
if (i == 5758) {
throw new IllegalArgumentException("oops");
}
return "Hello for " + i;
}

@SentinelResourceBinding(blockHandler = "globalBlockHandler", blockHandlerClass = FooUtil.class)
public int random() {
return ThreadLocalRandom.current().nextInt(0, 30000);
}

@SentinelResourceBinding(value = "apiBaz", blockHandler = "bazBlockHandler",
exceptionsToIgnore = {IllegalMonitorStateException.class})
public String baz(String name) {
if (name.equals("fail")) {
throw new IllegalMonitorStateException("boom!");
}
return "cheers, " + name;
}

public String fooBlockHandler(int i, BlockException ex) {
return "Oops, " + i;
}

public String fooFallbackFunc(int i) {
return "eee...";
}
}

+ 37
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/java/com/alibaba/csp/sentinel/annotation/cdi/interceptor/integration/service/FooUtil.java View File

@@ -0,0 +1,37 @@
/*
* Copyright 1999-2020 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.annotation.cdi.interceptor.integration.service;

import com.alibaba.csp.sentinel.slots.block.BlockException;

/**
* @author Eric Zhao
*/
public class FooUtil {

public static final int BLOCK_FLAG = 88888;
public static final String FALLBACK_DEFAULT_RESULT = "fallback";

public static int globalBlockHandler(BlockException ex) {
System.out.println("Oops: " + ex.getClass().getSimpleName());
return BLOCK_FLAG;
}

public static String globalDefaultFallback(Throwable t) {
System.out.println("Fallback caught: " + t.getClass().getSimpleName());
return FALLBACK_DEFAULT_RESULT;
}
}

+ 8
- 0
sentinel-extension/sentinel-annotation-cdi-interceptor/src/test/resources/META-INF/beans.xml View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
<interceptors>
<class>com.alibaba.csp.sentinel.annotation.cdi.interceptor.SentinelResourceInterceptor</class>
</interceptors>
</beans>

Loading…
Cancel
Save