SWS-702 - sws:annotation-driven does not pick up @Endpoint annotations on classes that are JDK proxies

This commit is contained in:
Arjen Poutsma
2011-10-18 11:13:40 +00:00
parent d9ed89fda7
commit 279daae037
13 changed files with 352 additions and 100 deletions

View File

@@ -17,12 +17,17 @@
package org.springframework.ws.server.endpoint.mapping;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContextException;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -129,19 +134,48 @@ public abstract class AbstractMethodEndpointMapping<T> extends AbstractEndpointM
*
* @see #getLookupKeyForMethod(Method)
*/
protected void registerMethods(final String beanName) {
protected void registerMethods(String beanName) {
Assert.hasText(beanName, "'beanName' must not be empty");
Class<?> endpointClass = getApplicationContext().getType(beanName);
ReflectionUtils.doWithMethods(endpointClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
T key = getLookupKeyForMethod(method);
if (key != null) {
registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), method));
}
Class<?> endpointType = getApplicationContext().getType(beanName);
endpointType = ClassUtils.getUserClass(endpointType);
Set<Method> methods = findEndpointMethods(endpointType, new ReflectionUtils.MethodFilter() {
public boolean matches(Method method) {
return getLookupKeyForMethod(method) != null;
}
});
for (Method method : methods) {
T key = getLookupKeyForMethod(method);
registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), method));
}
}
private Set<Method> findEndpointMethods(Class<?> endpointType,
final ReflectionUtils.MethodFilter endpointMethodFilter) {
final Set<Method> endpointMethods = new LinkedHashSet<Method>();
Set<Class<?>> endpointTypes = new LinkedHashSet<Class<?>>();
Class<?> specificEndpointType = null;
if (!Proxy.isProxyClass(endpointType)) {
endpointTypes.add(endpointType);
specificEndpointType = endpointType;
}
endpointTypes.addAll(Arrays.asList(endpointType.getInterfaces()));
for (Class<?> currentEndpointType : endpointTypes) {
final Class<?> targetClass = (specificEndpointType != null ? specificEndpointType : currentEndpointType);
ReflectionUtils.doWithMethods(currentEndpointType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (endpointMethodFilter.matches(specificMethod) &&
(bridgedMethod == specificMethod || !endpointMethodFilter.matches(bridgedMethod))) {
endpointMethods.add(specificMethod);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return endpointMethods;
}
/**

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2005-2010 the original author or authors.
* Copyright 2005-2011 the original author or authors.
*
* 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
* 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,
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import javax.xml.namespace.QName;
import javax.xml.transform.TransformerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointMapping;
@@ -60,7 +61,7 @@ public class PayloadRootAnnotationMethodEndpointMapping extends AbstractAnnotati
@Override
protected QName getLookupKeyForMethod(Method method) {
PayloadRoot annotation = method.getAnnotation(PayloadRoot.class);
PayloadRoot annotation = AnnotationUtils.findAnnotation(method, PayloadRoot.class);
if (annotation != null) {
QName qname;
if (StringUtils.hasLength(annotation.localPart()) && StringUtils.hasLength(annotation.namespace())) {

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2005-2010 the original author or authors.
* Copyright 2005-2011 the original author or authors.
*
* 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
* 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,
@@ -18,6 +18,7 @@ package org.springframework.ws.soap.server.endpoint.mapping;
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.ws.context.MessageContext;
@@ -105,7 +106,7 @@ public class SoapActionAnnotationMethodEndpointMapping extends AbstractAnnotatio
@Override
protected String getLookupKeyForMethod(Method method) {
SoapAction soapAction = method.getAnnotation(SoapAction.class);
SoapAction soapAction = AnnotationUtils.findAnnotation(method, SoapAction.class);
return soapAction != null ? soapAction.value() : null;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2005-2011 the original author or authors.
*
* 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 org.springframework.ws.server.endpoint.mapping;
import java.lang.reflect.Method;
import javax.xml.namespace.QName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("bridged-method-registration.xml")
public class BridgedMethodRegistrationTest {
@Autowired
private PayloadRootAnnotationMethodEndpointMapping mapping;
@Autowired
private ApplicationContext applicationContext;
@Test
public void registration() throws NoSuchMethodException {
MethodEndpoint bridgedMethod = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request"));
assertNotNull("bridged method endpoint not registered", bridgedMethod);
Method doIt = B.class.getMethod("doIt");
MethodEndpoint expected = new MethodEndpoint("bridgedMethodEndpoint", applicationContext, doIt);
assertEquals("Invalid endpoint registered", expected, bridgedMethod);
}
@Endpoint
public static class A {
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
public A doIt() {
return this;
}
}
public static class B extends A {
@Override
public B doIt() {
return this;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2005-2011 the original author or authors.
*
* 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 org.springframework.ws.server.endpoint.mapping;
import java.lang.reflect.Method;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("cglib-proxy-registration.xml")
public class CgLibProxyRegistrationTest {
@Autowired
private PayloadRootAnnotationMethodEndpointMapping mapping;
@Autowired
private ApplicationContext applicationContext;
@Test
public void registration() throws NoSuchMethodException {
MethodEndpoint cglibProxy = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request"));
assertNotNull("cg lib proxy endpoint not registered", cglibProxy);
Method doIt = MyEndpoint.class.getMethod("doIt", Source.class);
MethodEndpoint expected = new MethodEndpoint("cgLibProxyEndpoint", applicationContext, doIt);
assertEquals("Invalid endpoint registered", expected, cglibProxy);
}
@Endpoint
public static class MyEndpoint {
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
@Log
public void doIt(@RequestPayload Source payload) {
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2005-2011 the original author or authors.
*
* 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 org.springframework.ws.server.endpoint.mapping;
import java.lang.reflect.Method;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("jdk-proxy-registration.xml")
public class JdkProxyRegistrationTest {
@Autowired
private PayloadRootAnnotationMethodEndpointMapping mapping;
@Autowired
private ApplicationContext applicationContext;
@Test
public void registration() throws NoSuchMethodException {
MethodEndpoint jdkProxy = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request"));
assertNotNull("jdk proxy endpoint not registered", jdkProxy);
Method doIt = MyEndpointImpl.class.getMethod("doIt", Source.class);
MethodEndpoint expected = new MethodEndpoint("jdkProxyEndpoint", applicationContext, doIt);
assertEquals("Invalid endpoint registered", expected, jdkProxy);
}
@Endpoint
public interface MyEndpoint {
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
@Log
void doIt(Source payload);
}
public static class MyEndpointImpl implements MyEndpoint {
public void doIt(@RequestPayload Source payload) {
}
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2008 the original author or authors.
*
* 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 org.springframework.ws.server.endpoint.mapping;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
class OtherBean {
@PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws")
public void doIt() {
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2005-2010 the original author or authors.
* Copyright 2005-2011 the original author or authors.
*
* 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
* 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,
@@ -34,18 +34,21 @@ import org.springframework.ws.server.EndpointMapping;
import org.springframework.ws.server.MessageDispatcher;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter;
import org.springframework.ws.server.endpoint.adapter.PayloadMethodEndpointAdapter;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.soap.server.SoapMessageDispatcher;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("applicationContext.xml")
@ContextConfiguration("payloadRootAnnotationMethodEndpointMapping.xml")
public class PayloadRootAnnotationMethodEndpointMappingTest {
@Autowired
@@ -55,10 +58,10 @@ public class PayloadRootAnnotationMethodEndpointMappingTest {
private ApplicationContext applicationContext;
@Test
public void testRegistration() throws NoSuchMethodException {
public void registration() throws NoSuchMethodException {
MethodEndpoint endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request"));
assertNotNull("MethodEndpoint not registered", endpoint);
Method doIt = PayloadRootEndpoint.class.getMethod("doIt", Source.class);
Method doIt = MyEndpoint.class.getMethod("doIt", Source.class);
MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doIt);
assertEquals("Invalid endpoint registered", expected, endpoint);
@@ -67,7 +70,7 @@ public class PayloadRootAnnotationMethodEndpointMappingTest {
}
@Test
public void testInvoke() throws Exception {
public void invoke() throws Exception {
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage request = messageFactory.createMessage();
@@ -84,11 +87,39 @@ public class PayloadRootAnnotationMethodEndpointMappingTest {
messageDispatcher.receive(messageContext);
PayloadRootEndpoint endpoint = (PayloadRootEndpoint) applicationContext.getBean("endpoint");
MyEndpoint endpoint = applicationContext.getBean("endpoint", MyEndpoint.class);
assertTrue("doIt() not invoked on endpoint", endpoint.isDoItInvoked());
LogAspect aspect = (LogAspect) applicationContext.getBean("logAspect");
assertTrue("log() not invoked on aspect", aspect.isLogInvoked());
}
@Endpoint
public static class MyEndpoint {
private static final org.apache.commons.logging.Log logger = LogFactory.getLog(MyEndpoint.class);
private boolean doItInvoked = false;
public boolean isDoItInvoked() {
return doItInvoked;
}
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
@Log
public void doIt(@RequestPayload Source payload) {
doItInvoked = true;
logger.info("In doIt()");
}
}
static class OtherBean {
@PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws")
public void doIt() {
}
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2008 the original author or authors.
*
* 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 org.springframework.ws.server.endpoint.mapping;
import javax.xml.transform.Source;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
@Endpoint
public class PayloadRootEndpoint {
private static final Log logger = LogFactory.getLog(PayloadRootEndpoint.class);
private boolean doItInvoked = false;
public boolean isDoItInvoked() {
return doItInvoked;
}
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
@org.springframework.ws.server.endpoint.mapping.Log
public void doIt(@RequestPayload Source payload) {
doItInvoked = true;
logger.info("In doIt()");
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd">
<bean id="bridgedMethodEndpoint" class="org.springframework.ws.server.endpoint.mapping.BridgedMethodRegistrationTest$B"/>
<sws:annotation-driven/>
</beans>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd">
<bean id="cgLibProxyEndpoint" class="org.springframework.ws.server.endpoint.mapping.CgLibProxyRegistrationTest$MyEndpoint"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="logAspect" class="org.springframework.ws.server.endpoint.mapping.LogAspect"/>
<sws:annotation-driven/>
</beans>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd">
<bean id="jdkProxyEndpoint" class="org.springframework.ws.server.endpoint.mapping.JdkProxyRegistrationTest$MyEndpointImpl"/>
<aop:aspectj-autoproxy proxy-target-class="false"/>
<bean id="logAspect" class="org.springframework.ws.server.endpoint.mapping.LogAspect"/>
<sws:annotation-driven/>
</beans>

View File

@@ -7,11 +7,11 @@
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd">
<bean id="endpoint" class="org.springframework.ws.server.endpoint.mapping.PayloadRootEndpoint"/>
<bean id="endpoint" class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMappingTest$MyEndpoint"/>
<aop:aspectj-autoproxy/>
<bean id="other" class="org.springframework.ws.server.endpoint.mapping.OtherBean"/>
<bean id="other" class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMappingTest$OtherBean"/>
<bean id="logAspect" class="org.springframework.ws.server.endpoint.mapping.LogAspect"/>