Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -14,6 +14,8 @@ | |||
<properties> | |||
<aspectj.version>1.9.2</aspectj.version> | |||
<spring.test.version>5.1.5.RELEASE</spring.test.version> | |||
</properties> | |||
<dependencies> | |||
@@ -32,6 +34,40 @@ | |||
<artifactId>aspectjweaver</artifactId> | |||
<version>${aspectj.version}</version> | |||
</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> | |||
</project> | |||
</project> |
@@ -36,7 +36,7 @@ import java.util.Arrays; | |||
*/ | |||
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 (StringUtil.isNotBlank(resourceName)) { | |||
return resourceName; | |||
@@ -56,4 +56,18 @@ final class ResourceMetadataRegistry { | |||
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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |