Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -14,6 +14,8 @@ | |||||
<properties> | <properties> | ||||
<aspectj.version>1.9.2</aspectj.version> | <aspectj.version>1.9.2</aspectj.version> | ||||
<spring.test.version>5.1.5.RELEASE</spring.test.version> | |||||
</properties> | </properties> | ||||
<dependencies> | <dependencies> | ||||
@@ -32,6 +34,40 @@ | |||||
<artifactId>aspectjweaver</artifactId> | <artifactId>aspectjweaver</artifactId> | ||||
<version>${aspectj.version}</version> | <version>${aspectj.version}</version> | ||||
</dependency> | </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.mockito</groupId> | |||||
<artifactId>mockito-core</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-context</artifactId> | |||||
<version>${spring.test.version}</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-aop</artifactId> | |||||
<version>${spring.test.version}</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-test</artifactId> | |||||
<version>${spring.test.version}</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
</project> | |||||
</project> |
@@ -36,7 +36,7 @@ import java.util.Arrays; | |||||
*/ | */ | ||||
public abstract class AbstractSentinelAspectSupport { | public abstract class AbstractSentinelAspectSupport { | ||||
protected String getResourceName(String resourceName, Method method) { | |||||
protected String getResourceName(String resourceName, /*@NonNull*/ Method method) { | |||||
// If resource name is present in annotation, use this value. | // If resource name is present in annotation, use this value. | ||||
if (StringUtil.isNotBlank(resourceName)) { | if (StringUtil.isNotBlank(resourceName)) { | ||||
return resourceName; | return resourceName; | ||||
@@ -56,4 +56,18 @@ final class ResourceMetadataRegistry { | |||||
private static String getKey(Class<?> clazz, String name) { | private static String getKey(Class<?> clazz, String name) { | ||||
return String.format("%s:%s", clazz.getCanonicalName(), 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(); | |||||
} | |||||
} | } |
@@ -0,0 +1,39 @@ | |||||
/* | |||||
* 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.annotation.aspectj; | |||||
import com.alibaba.csp.sentinel.annotation.aspectj.integration.service.FooService; | |||||
import org.junit.Test; | |||||
import java.lang.reflect.Method; | |||||
import static org.assertj.core.api.Assertions.*; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
public class AbstractSentinelAspectSupportTest extends AbstractSentinelAspectSupport { | |||||
@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); | |||||
} | |||||
} |
@@ -0,0 +1,43 @@ | |||||
/* | |||||
* 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.annotation.aspectj; | |||||
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(); | |||||
} | |||||
} |
@@ -0,0 +1,85 @@ | |||||
/* | |||||
* 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.annotation.aspectj; | |||||
import com.alibaba.csp.sentinel.annotation.aspectj.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]); | |||||
} | |||||
} |
@@ -0,0 +1,124 @@ | |||||
/* | |||||
* 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.annotation.aspectj.integration; | |||||
import com.alibaba.csp.sentinel.annotation.aspectj.integration.config.AopTestConfig; | |||||
import com.alibaba.csp.sentinel.annotation.aspectj.integration.service.FooService; | |||||
import com.alibaba.csp.sentinel.annotation.aspectj.integration.service.FooUtil; | |||||
import com.alibaba.csp.sentinel.node.ClusterNode; | |||||
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.After; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import org.springframework.aop.support.AopUtils; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.test.context.ContextConfiguration; | |||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; | |||||
import java.lang.reflect.UndeclaredThrowableException; | |||||
import java.util.ArrayList; | |||||
import java.util.Collections; | |||||
import static org.assertj.core.api.Assertions.*; | |||||
/** | |||||
* Integration test for Sentinel annotation AspectJ extension. | |||||
* | |||||
* @author Eric Zhao | |||||
*/ | |||||
@ContextConfiguration(classes = {SentinelAnnotationIntegrationTest.class, AopTestConfig.class}) | |||||
public class SentinelAnnotationIntegrationTest extends AbstractJUnit4SpringContextTests { | |||||
@Autowired | |||||
private FooService fooService; | |||||
@Test | |||||
public void testProxySuccessful() { | |||||
assertThat(AopUtils.isAopProxy(fooService)).isTrue(); | |||||
assertThat(AopUtils.isCglibProxy(fooService)).isTrue(); | |||||
} | |||||
@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 = UndeclaredThrowableException.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 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 fallback. | |||||
assertThat(fooService.foo(9527)).isEqualTo("eee..."); | |||||
// Test for biz exception. | |||||
try { | |||||
fooService.foo(5758); | |||||
fail("should not reach here"); | |||||
} catch (IllegalAccessException 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(); | |||||
} | |||||
@Before | |||||
public void setUp() throws Exception { | |||||
FlowRuleManager.loadRules(new ArrayList<FlowRule>()); | |||||
ClusterBuilderSlot.resetClusterNodes(); | |||||
} | |||||
@After | |||||
public void tearDown() throws Exception { | |||||
FlowRuleManager.loadRules(new ArrayList<FlowRule>()); | |||||
ClusterBuilderSlot.resetClusterNodes(); | |||||
} | |||||
} |
@@ -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.annotation.aspectj.integration.config; | |||||
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.ComponentScan; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.context.annotation.EnableAspectJAutoProxy; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Configuration | |||||
@EnableAspectJAutoProxy(proxyTargetClass = true) | |||||
@ComponentScan("com.alibaba.csp.sentinel.annotation.aspectj.integration") | |||||
public class AopTestConfig { | |||||
@Bean | |||||
public SentinelResourceAspect sentinelResourceAspect() { | |||||
return new SentinelResourceAspect(); | |||||
} | |||||
} |
@@ -0,0 +1,59 @@ | |||||
/* | |||||
* 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.annotation.aspectj.integration.service; | |||||
import com.alibaba.csp.sentinel.annotation.SentinelResource; | |||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; | |||||
import org.springframework.stereotype.Service; | |||||
import java.util.concurrent.ThreadLocalRandom; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Service | |||||
public class FooService { | |||||
@SentinelResource(value = "apiFoo", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc") | |||||
public String foo(int i) throws Exception { | |||||
if (i == 9527) { | |||||
throw new DegradeException("ggg"); | |||||
} | |||||
if (i == 5758) { | |||||
throw new IllegalAccessException(); | |||||
} | |||||
return "Hello for " + i; | |||||
} | |||||
@SentinelResource(blockHandler = "globalBlockHandler", blockHandlerClass = FooUtil.class) | |||||
public int random() { | |||||
return ThreadLocalRandom.current().nextInt(0, 30000); | |||||
} | |||||
@SentinelResource(value = "apiBaz", blockHandler = "bazBlockHandler") | |||||
public String baz(String name) { | |||||
return "cheers, " + name; | |||||
} | |||||
public String fooBlockHandler(int i, BlockException ex) { | |||||
return "Oops, " + i; | |||||
} | |||||
public String fooFallbackFunc(int i) { | |||||
return "eee..."; | |||||
} | |||||
} |
@@ -0,0 +1,31 @@ | |||||
/* | |||||
* 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.annotation.aspectj.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 int globalBlockHandler(BlockException ex) { | |||||
System.out.println("Oops: " + ex.getClass().getSimpleName()); | |||||
return BLOCK_FLAG; | |||||
} | |||||
} |