Skip to content

Commit 55e6e3a

Browse files
committed
SWS-273
1 parent 9a42406 commit 55e6e3a

File tree

7 files changed

+162
-45
lines changed

7 files changed

+162
-45
lines changed

core-tiger/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationMethodEndpointMapping.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,56 @@
1919
import java.lang.annotation.Annotation;
2020

2121
import org.springframework.beans.BeansException;
22-
import org.springframework.beans.factory.config.BeanPostProcessor;
22+
import org.springframework.beans.factory.BeanFactoryUtils;
2323
import org.springframework.ws.server.endpoint.annotation.Endpoint;
2424

2525
/**
2626
* Abstract base for {@link org.springframework.ws.server.EndpointMapping} implementations that map classes tagged with
2727
* an annotation. By default the annotation is {@link Endpoint}, but this can be overriden in subclasses.
2828
* <p/>
29-
* The methods of each bean carrying @Endpoint will be registered using {@link #registerMethods(Object)}.
29+
* The methods of each bean carrying @Endpoint will be registered using {@link #registerMethods(String)}.
3030
*
3131
* @author Arjen Poutsma
3232
* @since 1.0.0
3333
*/
34-
public abstract class AbstractAnnotationMethodEndpointMapping extends AbstractMethodEndpointMapping
35-
implements BeanPostProcessor {
34+
public abstract class AbstractAnnotationMethodEndpointMapping extends AbstractMethodEndpointMapping {
3635

37-
public final Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
38-
return bean;
36+
private boolean detectEndpointsInAncestorContexts = false;
37+
38+
/**
39+
* Set whether to detect endpoint beans in ancestor ApplicationContexts.
40+
* <p/>
41+
* Default is "false": Only endpoint beans in the current ApplicationContext will be detected, i.e. only in the
42+
* context that this EndpointMapping itself is defined in (typically the current MessageDispatcherServlet's
43+
* context).
44+
* <p/>
45+
* Switch this flag on to detect endpoint beans in ancestor contexts (typically the Spring root
46+
* WebApplicationContext) as well.
47+
*/
48+
public void setDetectEndpointsInAncestorContexts(boolean detectEndpointsInAncestorContexts) {
49+
this.detectEndpointsInAncestorContexts = detectEndpointsInAncestorContexts;
3950
}
4051

4152
/** Returns the 'endpoint' annotation type. Default is {@link Endpoint}. */
4253
protected Class<? extends Annotation> getEndpointAnnotationType() {
4354
return Endpoint.class;
4455
}
4556

46-
public final Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
47-
Class endpointClass = getEndpointClass(bean);
48-
if (endpointClass != null && endpointClass.getAnnotation(getEndpointAnnotationType()) != null) {
49-
registerMethods(bean);
57+
protected final void initApplicationContext() throws BeansException {
58+
if (logger.isDebugEnabled()) {
59+
logger.debug("Looking for endpoints in application context: " + getApplicationContext());
60+
}
61+
String[] beanNames = (this.detectEndpointsInAncestorContexts ?
62+
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
63+
getApplicationContext().getBeanNamesForType(Object.class));
64+
65+
for (int i = 0; i < beanNames.length; i++) {
66+
String beanName = beanNames[i];
67+
Class endpointClass = getApplicationContext().getType(beanName);
68+
if (endpointClass != null && endpointClass.getAnnotation(getEndpointAnnotationType()) != null) {
69+
registerMethods(beanName);
70+
}
5071
}
51-
return bean;
5272
}
5373

5474
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2008 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.server.endpoint.mapping;
18+
19+
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
20+
21+
class OtherBean {
22+
23+
@PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws")
24+
public void doIt() {
25+
26+
}
27+
28+
}

core-tiger/src/test/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMappingTest.java

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616

1717
package org.springframework.ws.server.endpoint.mapping;
1818

19+
import java.lang.reflect.Method;
20+
1921
import junit.framework.TestCase;
22+
2023
import org.springframework.context.support.StaticApplicationContext;
2124
import org.springframework.ws.server.endpoint.MethodEndpoint;
22-
import org.springframework.ws.server.endpoint.annotation.Endpoint;
23-
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
2425

2526
public class PayloadRootAnnotationMethodEndpointMappingTest extends TestCase {
2627

@@ -31,7 +32,7 @@ public class PayloadRootAnnotationMethodEndpointMappingTest extends TestCase {
3132
protected void setUp() throws Exception {
3233
applicationContext = new StaticApplicationContext();
3334
applicationContext.registerSingleton("mapping", PayloadRootAnnotationMethodEndpointMapping.class);
34-
applicationContext.registerSingleton("endpoint", MyEndpoint.class);
35+
applicationContext.registerSingleton("endpoint", PayloadRootEndpoint.class);
3536
applicationContext.registerSingleton("other", OtherBean.class);
3637
applicationContext.refresh();
3738
mapping = (PayloadRootAnnotationMethodEndpointMapping) applicationContext.getBean("mapping");
@@ -40,30 +41,12 @@ protected void setUp() throws Exception {
4041
public void testRegistration() throws NoSuchMethodException {
4142
MethodEndpoint endpoint = mapping.lookupEndpoint("{http://springframework.org/spring-ws}Request");
4243
assertNotNull("MethodEndpoint not registered", endpoint);
43-
MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
44+
Method doIt = PayloadRootEndpoint.class.getMethod("doIt", new Class[0]);
45+
MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doIt);
4446
assertEquals("Invalid endpoint registered", expected, endpoint);
4547

4648
assertNull("Invalid endpoint registered",
4749
mapping.lookupEndpoint("{http://springframework.org/spring-ws}Request2"));
4850
}
4951

50-
@Endpoint
51-
private static class MyEndpoint {
52-
53-
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
54-
public void doIt() {
55-
56-
}
57-
58-
}
59-
60-
private static class OtherBean {
61-
62-
@PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws")
63-
public void doIt() {
64-
65-
}
66-
67-
}
68-
6952
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2008 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.server.endpoint.mapping;
18+
19+
import org.springframework.ws.server.endpoint.annotation.Endpoint;
20+
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
21+
22+
@Endpoint
23+
class PayloadRootEndpoint {
24+
25+
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
26+
public void doIt() {
27+
28+
}
29+
30+
}

core-tiger/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMappingTest.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
package org.springframework.ws.soap.server.endpoint.mapping;
1818

19+
import java.lang.reflect.Method;
20+
1921
import junit.framework.TestCase;
2022
import static org.easymock.EasyMock.*;
23+
2124
import org.springframework.context.support.StaticApplicationContext;
2225
import org.springframework.ws.WebServiceMessageFactory;
2326
import org.springframework.ws.context.DefaultMessageContext;
@@ -43,18 +46,19 @@ protected void setUp() throws Exception {
4346
}
4447

4548
public void testRegistration() throws Exception {
46-
SoapMessage requestMock = createMock(SoapMessage.class);
47-
expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction");
48-
WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class);
49-
replay(requestMock, factoryMock);
49+
SoapMessage requestMock = createMock(SoapMessage.class);
50+
expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction");
51+
WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class);
52+
replay(requestMock, factoryMock);
5053

51-
MessageContext context = new DefaultMessageContext(requestMock, factoryMock);
54+
MessageContext context = new DefaultMessageContext(requestMock, factoryMock);
5255
EndpointInvocationChain chain = mapping.getEndpoint(context);
5356
assertNotNull("MethodEndpoint not registered", chain);
54-
MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
57+
Method doIt = MyEndpoint.class.getMethod("doIt", new Class[0]);
58+
MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doIt);
5559
assertEquals("Invalid endpoint registered", expected, chain.getEndpoint());
56-
57-
verify(requestMock,factoryMock);
60+
61+
verify(requestMock, factoryMock);
5862
}
5963

6064
@Endpoint

core/src/main/java/org/springframework/ws/server/endpoint/MethodEndpoint.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.lang.reflect.InvocationTargetException;
2020
import java.lang.reflect.Method;
2121

22+
import org.springframework.beans.factory.BeanFactory;
2223
import org.springframework.core.JdkVersion;
2324
import org.springframework.util.Assert;
2425

@@ -32,9 +33,11 @@
3233
*/
3334
public final class MethodEndpoint {
3435

35-
private Object bean;
36+
private final Object bean;
3637

37-
private Method method;
38+
private final Method method;
39+
40+
private final BeanFactory beanFactory;
3841

3942
/**
4043
* Constructs a new method endpoint with the given bean and method.
@@ -47,6 +50,7 @@ public MethodEndpoint(Object bean, Method method) {
4750
Assert.notNull(method, "method must not be null");
4851
this.bean = bean;
4952
this.method = method;
53+
this.beanFactory = null;
5054
}
5155

5256
/**
@@ -62,6 +66,26 @@ public MethodEndpoint(Object bean, String methodName, Class[] parameterTypes) th
6266
Assert.notNull(methodName, "method must not be null");
6367
this.bean = bean;
6468
this.method = bean.getClass().getMethod(methodName, parameterTypes);
69+
this.beanFactory = null;
70+
}
71+
72+
/**
73+
* Constructs a new method endpoint with the given bean name and method. The bean name will be lazily initized when
74+
* {@link #invoke(Object[])} is called.
75+
*
76+
* @param beanName the bean name
77+
* @param beanFactory the bean factory to use for bean initialization
78+
* @param method the method
79+
*/
80+
public MethodEndpoint(String beanName, BeanFactory beanFactory, Method method) {
81+
Assert.hasText(beanName, "'beanName' must not be null");
82+
Assert.notNull(beanFactory, "'beanFactory' must not be null");
83+
Assert.notNull(method, "'method' must not be null");
84+
Assert.isTrue(beanFactory.containsBean(beanName),
85+
"Bean factory [" + beanFactory + "] does not contain bean " + "with name [" + beanName + "]");
86+
this.bean = beanName;
87+
this.beanFactory = beanFactory;
88+
this.method = method;
6589
}
6690

6791
/** Returns the object bean for this method endpoint. */
@@ -82,8 +106,13 @@ public Method getMethod() {
82106
* @throws Exception when the method invocation results in an exception
83107
*/
84108
public Object invoke(Object[] args) throws Exception {
109+
Object endpoint = bean;
110+
if (endpoint instanceof String) {
111+
String endpointName = (String) endpoint;
112+
endpoint = beanFactory.getBean(endpointName);
113+
}
85114
try {
86-
return this.method.invoke(this.bean, args);
115+
return this.method.invoke(endpoint, args);
87116
}
88117
catch (InvocationTargetException ex) {
89118
handleInvocationTargetException(ex);

core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,29 @@ protected void registerMethods(Object endpoint) {
124124
}
125125
}
126126

127+
/**
128+
* Helper method that registers the methods of the given class. This method iterates over the methods of the class,
129+
* and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
130+
* using {@link #registerEndpoint(String,MethodEndpoint)}.
131+
*
132+
* @see #getLookupKeyForMethod(Method)
133+
*/
134+
protected void registerMethods(String beanName) {
135+
Assert.hasText(beanName, "'beanName' must not be empty");
136+
Class endpointClass = getApplicationContext().getType(beanName);
137+
Method[] methods = endpointClass.getMethods();
138+
for (int i = 0; i < methods.length; i++) {
139+
if (JdkVersion.isAtLeastJava15() && methods[i].isSynthetic() ||
140+
methods[i].getDeclaringClass().equals(Object.class)) {
141+
continue;
142+
}
143+
String key = getLookupKeyForMethod(methods[i]);
144+
if (StringUtils.hasLength(key)) {
145+
registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), methods[i]));
146+
}
147+
}
148+
}
149+
127150
/**
128151
* Returns the the endpoint keys for the given method. Returns <code>null</code> if the method is not to be
129152
* registered, which is the default.

0 commit comments

Comments
 (0)