Skip to content

Commit a982a1d

Browse files
committed
Bugfix
1 parent d54f0fe commit a982a1d

File tree

7 files changed

+218
-57
lines changed

7 files changed

+218
-57
lines changed

projects/stage-1/middleware-frameworks/my-interceptor/src/main/java/org/geektimes/interceptor/ChainableInvocationContext.java

+19-10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import javax.interceptor.InvocationContext;
2222
import java.lang.reflect.Constructor;
2323
import java.lang.reflect.Method;
24+
import java.util.Collection;
2425
import java.util.List;
2526
import java.util.Map;
2627

@@ -92,8 +93,12 @@ public Object proceed() throws Exception {
9293
if (pos < size) {
9394
int currentPos = pos++;
9495
Object interceptor = interceptors.get(currentPos);
95-
Method interceptionMethod = resolveInterceptionMethod(interceptor);
96-
return interceptionMethod.invoke(interceptor, this);
96+
Collection<Method> interceptionMethods = resolveInterceptionMethods(interceptor);
97+
Object result = null;
98+
for (Method interceptionMethod : interceptionMethods) {
99+
result = interceptionMethod.invoke(interceptor, this);
100+
}
101+
return result;
97102
} else {
98103
return delegateContext.proceed();
99104
}
@@ -113,26 +118,30 @@ private List<Object> resolveInterceptors(Object[] defaultInterceptors) {
113118
}
114119

115120

116-
private Method resolveInterceptionMethod(Object interceptor) {
121+
private Collection<Method> resolveInterceptionMethods(Object interceptor) {
117122
InterceptorInfo interceptorInfo = interceptorManager.getInterceptorInfo(interceptor.getClass());
118123

119-
final Method interceptionMethod; // nerver null
124+
if (interceptorInfo == null) { // interceptor may be a default(external) Interceptor
125+
interceptorInfo = new InterceptorInfo(interceptor.getClass());
126+
}
127+
128+
final Collection<Method> interceptionMethods; // nerver null
120129

121130
if (getTimer() != null) { // If the "Timer" is present
122-
interceptionMethod = interceptorInfo.getAroundTimeoutMethod();
131+
interceptionMethods = interceptorInfo.getAroundTimeoutMethods();
123132
} else if (getConstructor() != null) { // If the "Constructor" should be intercepted
124-
interceptionMethod = interceptorInfo.getAroundConstructMethod();
133+
interceptionMethods = interceptorInfo.getAroundConstructMethods();
125134
} else {
126135
Method method = getMethod();
127136
if (method.isAnnotationPresent(PostConstruct.class)) {
128-
interceptionMethod = interceptorInfo.getPostConstructMethod();
137+
interceptionMethods = interceptorInfo.getPostConstructMethods();
129138
} else if (method.isAnnotationPresent(PreDestroy.class)) {
130-
interceptionMethod = interceptorInfo.getPreDestroyMethod();
139+
interceptionMethods = interceptorInfo.getPreDestroyMethods();
131140
} else {
132-
interceptionMethod = interceptorInfo.getAroundInvokeMethod();
141+
interceptionMethods = interceptorInfo.getAroundInvokeMethods();
133142
}
134143
}
135144

136-
return interceptionMethod;
145+
return interceptionMethods;
137146
}
138147
}

projects/stage-1/middleware-frameworks/my-interceptor/src/main/java/org/geektimes/interceptor/DefaultInterceptorManager.java

+29-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,35 @@ public void registerInterceptorClass(Class<?> interceptorClass) {
8383
public void registerInterceptor(Object interceptor) {
8484
Class<?> interceptorClass = interceptor.getClass();
8585
registerInterceptorClass(interceptorClass);
86-
InterceptorBindings interceptorBindings = getInterceptorBindings(interceptorClass);
86+
InterceptorInfo interceptorInfo = getInterceptorInfo(interceptorClass);
87+
registerRegularInterceptor(interceptorInfo, interceptor);
88+
registerLifecycleEventInterceptor(interceptorInfo, interceptor);
89+
}
90+
91+
private void registerRegularInterceptor(InterceptorInfo interceptorInfo, Object interceptor) {
92+
InterceptorBindings interceptorBindings = interceptorInfo.getInterceptorBindings();
93+
registerInterceptor(interceptorBindings, interceptor);
94+
}
95+
96+
private void registerLifecycleEventInterceptor(InterceptorInfo interceptorInfo, Object interceptor) {
97+
for (Method method : interceptorInfo.getPostConstructMethods()) {
98+
registerLifecycleEventInterceptor(method, PostConstruct.class, interceptor);
99+
}
100+
101+
for (Method method : interceptorInfo.getPreDestroyMethods()) {
102+
registerLifecycleEventInterceptor(method, PreDestroy.class, interceptor);
103+
}
104+
}
105+
106+
private void registerLifecycleEventInterceptor(Method method, Class<? extends Annotation> lifecycleAnnotationType, Object interceptor) {
107+
Annotation lifecycleAnnotation = method.getAnnotation(lifecycleAnnotationType);
108+
if (lifecycleAnnotation != null) {
109+
InterceptorBindings interceptorBindings = new InterceptorBindings(singleton(lifecycleAnnotation));
110+
registerInterceptor(interceptorBindings, interceptor);
111+
}
112+
}
113+
114+
private void registerInterceptor(InterceptorBindings interceptorBindings, Object interceptor) {
87115
SortedSet<Object> interceptors = bindingInterceptors.computeIfAbsent(interceptorBindings, t -> new TreeSet<>(PriorityComparator.INSTANCE));
88116
interceptors.add(interceptor);
89117
}

projects/stage-1/middleware-frameworks/my-interceptor/src/main/java/org/geektimes/interceptor/InterceptorInfo.java

+64-44
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@
2525
import javax.interceptor.AroundTimeout;
2626
import java.lang.annotation.Annotation;
2727
import java.lang.reflect.Method;
28-
import java.util.HashMap;
29-
import java.util.Map;
30-
import java.util.Set;
28+
import java.util.*;
29+
import java.util.function.Consumer;
3130
import java.util.function.Predicate;
3231

3332
import static java.lang.String.format;
34-
import static org.geektimes.commons.collection.util.CollectionUtils.asSet;
33+
import static java.util.Collections.unmodifiableCollection;
3534
import static org.geektimes.commons.lang.util.AnnotationUtils.getAllDeclaredAnnotations;
36-
import static org.geektimes.commons.reflect.util.MethodUtils.getAllDeclaredMethods;
35+
import static org.geektimes.commons.reflect.util.ClassUtils.getAllClasses;
3736
import static org.geektimes.interceptor.util.InterceptorUtils.validatorInterceptorClass;
3837

3938
/**
@@ -48,59 +47,80 @@ public class InterceptorInfo {
4847

4948
private final Class<?> interceptorClass;
5049

51-
private final Method aroundInvokeMethod;
50+
/**
51+
* If an interceptor class declared using interceptor bindings has superclasses,
52+
* interceptor methods declared in the interceptor class’s superclasses are
53+
* invoked before the interceptor method declared in the interceptor class itself,
54+
* most general superclass first.
55+
*/
56+
private final Collection<Method> aroundInvokeMethods;
5257

53-
private final Method aroundTimeoutMethod;
58+
private final Collection<Method> aroundTimeoutMethods;
5459

55-
private final Method aroundConstructMethod;
60+
private final Collection<Method> aroundConstructMethods;
5661

57-
private final Method postConstructMethod;
62+
private final Collection<Method> postConstructMethods;
5863

59-
private final Method preDestroyMethod;
64+
private final Collection<Method> preDestroyMethods;
6065

6166
private final InterceptorBindings interceptorBindings;
6267

6368
public InterceptorInfo(Class<?> interceptorClass) {
6469
validatorInterceptorClass(interceptorClass);
6570
this.interceptorManager = InterceptorManager.getInstance(interceptorClass.getClassLoader());
6671
this.interceptorClass = interceptorClass;
67-
Map<Class<? extends Annotation>, Method> interceptionMethods = resolveInterceptionMethods();
68-
this.aroundInvokeMethod = interceptionMethods.remove(AroundInvoke.class);
69-
this.aroundTimeoutMethod = interceptionMethods.remove(AroundTimeout.class);
70-
this.aroundConstructMethod = interceptionMethods.remove(AroundConstruct.class);
71-
this.postConstructMethod = interceptionMethods.remove(PostConstruct.class);
72-
this.preDestroyMethod = interceptionMethods.remove(PreDestroy.class);
72+
this.aroundInvokeMethods = new LinkedList<>();
73+
this.aroundTimeoutMethods = new LinkedList<>();
74+
this.aroundConstructMethods = new LinkedList<>();
75+
this.postConstructMethods = new LinkedList<>();
76+
this.preDestroyMethods = new LinkedList<>();
77+
resolveInterceptionMethods();
7378
this.interceptorBindings = resolveInterceptorBindings();
7479
}
7580

76-
private Map<Class<? extends Annotation>, Method> resolveInterceptionMethods() throws IllegalStateException {
77-
Set<Method> methods = getAllDeclaredMethods(interceptorClass, method -> !Object.class.equals(method.getDeclaringClass()));
78-
Map<Class<? extends Annotation>, Method> interceptionMethods = new HashMap<>();
81+
private void resolveInterceptionMethods() throws IllegalStateException {
82+
Set<Class<?>> allClasses = getAllClasses(interceptorClass, true, t -> !Object.class.equals(t));
7983

80-
for (Method method : methods) {
81-
resolveInterceptionMethod(method, AroundInvoke.class, InterceptorUtils::isAroundInvokeMethod, interceptionMethods);
82-
resolveInterceptionMethod(method, AroundTimeout.class, InterceptorUtils::isAroundTimeoutMethod, interceptionMethods);
83-
resolveInterceptionMethod(method, AroundConstruct.class, InterceptorUtils::isAroundConstructMethod, interceptionMethods);
84-
resolveInterceptionMethod(method, PostConstruct.class, InterceptorUtils::isPostConstructMethod, interceptionMethods);
85-
resolveInterceptionMethod(method, PreDestroy.class, InterceptorUtils::isPreDestroyMethod, interceptionMethods);
84+
for (Class<?> declaringClass : allClasses) {
85+
Map<Class<? extends Annotation>, Method> interceptionMethods = new HashMap<>();
86+
for (Method method : declaringClass.getDeclaredMethods()) {
87+
resolveInterceptionMethod(method, AroundInvoke.class, InterceptorUtils::isAroundInvokeMethod,
88+
interceptionMethods, aroundInvokeMethods::add);
89+
90+
resolveInterceptionMethod(method, AroundTimeout.class, InterceptorUtils::isAroundTimeoutMethod,
91+
interceptionMethods, aroundTimeoutMethods::add);
92+
93+
resolveInterceptionMethod(method, AroundConstruct.class, InterceptorUtils::isAroundConstructMethod,
94+
interceptionMethods, aroundConstructMethods::add);
95+
96+
resolveInterceptionMethod(method, PostConstruct.class, InterceptorUtils::isPostConstructMethod,
97+
interceptionMethods, postConstructMethods::add);
98+
99+
resolveInterceptionMethod(method, PreDestroy.class, InterceptorUtils::isPreDestroyMethod,
100+
interceptionMethods, preDestroyMethods::add);
101+
}
102+
interceptionMethods.clear();
86103
}
87104

88-
return interceptionMethods;
89105
}
90106

91-
private void resolveInterceptionMethod(Method method, Class<? extends Annotation> annotationType,
107+
private void resolveInterceptionMethod(Method method,
108+
Class<? extends Annotation> annotationType,
92109
Predicate<Method> isInterceptionMethod,
93-
Map<Class<? extends Annotation>, Method> interceptionMethods) {
110+
Map<Class<? extends Annotation>, Method> interceptionMethods,
111+
Consumer<Method> interceptionMethodConsumer) {
94112
if (isInterceptionMethod.test(method)) {
95-
if (interceptionMethods.putIfAbsent(annotationType, method) != null) {
96-
throw interceptionMethodDefinitionException(annotationType);
113+
if (interceptionMethods.putIfAbsent(annotationType, method) == null) {
114+
interceptionMethodConsumer.accept(method);
115+
} else {
116+
throw interceptionMethodDefinitionException(method, annotationType);
97117
}
98118
}
99119
}
100120

101-
private IllegalStateException interceptionMethodDefinitionException(Class<? extends Annotation> annotationType) {
102-
throw new IllegalStateException(format("There is only one @%s method is declared in the interceptor class[%s]",
103-
annotationType.getName(), interceptorClass.getName()));
121+
private IllegalStateException interceptionMethodDefinitionException(Method method, Class<? extends Annotation> annotationType) {
122+
throw new IllegalStateException(format("There is only one @%s method[%s] is declared in the interceptor class[%s]",
123+
annotationType.getName(), method.toString(), method.getDeclaringClass().getName()));
104124
}
105125

106126
private InterceptorBindings resolveInterceptorBindings() {
@@ -111,27 +131,27 @@ public Class<?> getInterceptorClass() {
111131
return interceptorClass;
112132
}
113133

114-
public Method getAroundInvokeMethod() {
115-
return aroundInvokeMethod;
134+
Collection<Method> getAroundInvokeMethods() {
135+
return aroundInvokeMethods;
116136
}
117137

118-
public Method getAroundTimeoutMethod() {
119-
return aroundTimeoutMethod;
138+
Collection<Method> getAroundTimeoutMethods() {
139+
return aroundTimeoutMethods;
120140
}
121141

122-
public Method getAroundConstructMethod() {
123-
return aroundConstructMethod;
142+
Collection<Method> getAroundConstructMethods() {
143+
return aroundConstructMethods;
124144
}
125145

126-
public Method getPostConstructMethod() {
127-
return postConstructMethod;
146+
Collection<Method> getPostConstructMethods() {
147+
return postConstructMethods;
128148
}
129149

130-
public Method getPreDestroyMethod() {
131-
return preDestroyMethod;
150+
Collection<Method> getPreDestroyMethods() {
151+
return preDestroyMethods;
132152
}
133153

134-
public InterceptorBindings getInterceptorBindings() {
154+
InterceptorBindings getInterceptorBindings() {
135155
return interceptorBindings;
136156
}
137157

projects/stage-1/middleware-frameworks/my-interceptor/src/test/java/org/geektimes/interceptor/DefaultComponentEnhancerTest.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import org.junit.Test;
2020

21+
import static org.geektimes.commons.collection.util.CollectionUtils.asSet;
22+
import static org.junit.Assert.assertEquals;
23+
2124
/**
2225
* {@link DefaultComponentEnhancer} Test
2326
*
@@ -31,8 +34,11 @@ public class DefaultComponentEnhancerTest {
3134
@Test
3235
public void testInterface() {
3336
EchoService echoService = new EchoService();
34-
echoService = interceptorEnhancer.enhance(echoService);
37+
ExternalInterceptor interceptor = new ExternalInterceptor();
38+
echoService = interceptorEnhancer.enhance(echoService, interceptor);
3539
echoService.init();
3640
echoService.echo("Hello,World");
41+
42+
assertEquals(asSet("init", "echo"), interceptor.getMethodNames());
3743
}
3844
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.geektimes.interceptor;
18+
19+
import org.geektimes.interceptor.Logging;
20+
import org.geektimes.interceptor.LoggingInterceptor;
21+
22+
import javax.annotation.PostConstruct;
23+
import javax.interceptor.Interceptor;
24+
import javax.interceptor.InvocationContext;
25+
import java.util.logging.Logger;
26+
27+
/**
28+
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
29+
* @since
30+
*/
31+
@Logging
32+
@Interceptor
33+
public class ExtLoggingInterceptor extends LoggingInterceptor {
34+
35+
@PostConstruct
36+
public void postConstruct(InvocationContext context) throws Exception {
37+
Logger logger = Logger.getLogger(getClass().getName());
38+
logger.info("postConstruct");
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.geektimes.interceptor;
18+
19+
import javax.annotation.PostConstruct;
20+
import javax.interceptor.AroundInvoke;
21+
import javax.interceptor.Interceptor;
22+
import javax.interceptor.InvocationContext;
23+
import java.util.LinkedHashSet;
24+
import java.util.Set;
25+
import java.util.logging.Logger;
26+
27+
/**
28+
* External {@link Interceptor @Interceptor}
29+
*
30+
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
31+
* @since 1.0.0
32+
*/
33+
@Interceptor
34+
public class ExternalInterceptor {
35+
36+
private final Logger logger = Logger.getLogger(getClass().getName());
37+
38+
private Set<String> methodNames = new LinkedHashSet<>();
39+
40+
@AroundInvoke
41+
public Object intercept(InvocationContext context) throws Throwable {
42+
String methodName = context.getMethod().getName();
43+
methodNames.add(methodName);
44+
logger.info("Interception Method : " + methodName);
45+
return context.proceed();
46+
}
47+
48+
@PostConstruct
49+
public void postConstruct(InvocationContext context) {
50+
String methodName = context.getMethod().getName();
51+
methodNames.add(methodName);
52+
logger.info("Post Construct : " + context.getMethod().getName());
53+
}
54+
55+
public Set<String> getMethodNames() {
56+
return methodNames;
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
org.geektimes.interceptor.LoggingInterceptor
1+
org.geektimes.interceptor.ExtLoggingInterceptor

0 commit comments

Comments
 (0)