This commit is contained in:
Arjen Poutsma
2008-07-03 22:20:12 +00:00
parent 9a424067a8
commit 55e6e3a185
7 changed files with 162 additions and 45 deletions

View File

@@ -19,23 +19,34 @@ package org.springframework.ws.server.endpoint.mapping;
import java.lang.annotation.Annotation;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
/**
* Abstract base for {@link org.springframework.ws.server.EndpointMapping} implementations that map classes tagged with
* an annotation. By default the annotation is {@link Endpoint}, but this can be overriden in subclasses.
* <p/>
* The methods of each bean carrying @Endpoint will be registered using {@link #registerMethods(Object)}.
* The methods of each bean carrying @Endpoint will be registered using {@link #registerMethods(String)}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
public abstract class AbstractAnnotationMethodEndpointMapping extends AbstractMethodEndpointMapping
implements BeanPostProcessor {
public abstract class AbstractAnnotationMethodEndpointMapping extends AbstractMethodEndpointMapping {
public final Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
private boolean detectEndpointsInAncestorContexts = false;
/**
* Set whether to detect endpoint beans in ancestor ApplicationContexts.
* <p/>
* Default is "false": Only endpoint beans in the current ApplicationContext will be detected, i.e. only in the
* context that this EndpointMapping itself is defined in (typically the current MessageDispatcherServlet's
* context).
* <p/>
* Switch this flag on to detect endpoint beans in ancestor contexts (typically the Spring root
* WebApplicationContext) as well.
*/
public void setDetectEndpointsInAncestorContexts(boolean detectEndpointsInAncestorContexts) {
this.detectEndpointsInAncestorContexts = detectEndpointsInAncestorContexts;
}
/** Returns the 'endpoint' annotation type. Default is {@link Endpoint}. */
@@ -43,12 +54,21 @@ public abstract class AbstractAnnotationMethodEndpointMapping extends AbstractMe
return Endpoint.class;
}
public final Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class endpointClass = getEndpointClass(bean);
if (endpointClass != null && endpointClass.getAnnotation(getEndpointAnnotationType()) != null) {
registerMethods(bean);
protected final void initApplicationContext() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for endpoints in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectEndpointsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (int i = 0; i < beanNames.length; i++) {
String beanName = beanNames[i];
Class endpointClass = getApplicationContext().getType(beanName);
if (endpointClass != null && endpointClass.getAnnotation(getEndpointAnnotationType()) != null) {
registerMethods(beanName);
}
}
return bean;
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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

@@ -16,11 +16,12 @@
package org.springframework.ws.server.endpoint.mapping;
import java.lang.reflect.Method;
import junit.framework.TestCase;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
public class PayloadRootAnnotationMethodEndpointMappingTest extends TestCase {
@@ -31,7 +32,7 @@ public class PayloadRootAnnotationMethodEndpointMappingTest extends TestCase {
protected void setUp() throws Exception {
applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("mapping", PayloadRootAnnotationMethodEndpointMapping.class);
applicationContext.registerSingleton("endpoint", MyEndpoint.class);
applicationContext.registerSingleton("endpoint", PayloadRootEndpoint.class);
applicationContext.registerSingleton("other", OtherBean.class);
applicationContext.refresh();
mapping = (PayloadRootAnnotationMethodEndpointMapping) applicationContext.getBean("mapping");
@@ -40,30 +41,12 @@ public class PayloadRootAnnotationMethodEndpointMappingTest extends TestCase {
public void testRegistration() throws NoSuchMethodException {
MethodEndpoint endpoint = mapping.lookupEndpoint("{http://springframework.org/spring-ws}Request");
assertNotNull("MethodEndpoint not registered", endpoint);
MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
Method doIt = PayloadRootEndpoint.class.getMethod("doIt", new Class[0]);
MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doIt);
assertEquals("Invalid endpoint registered", expected, endpoint);
assertNull("Invalid endpoint registered",
mapping.lookupEndpoint("{http://springframework.org/spring-ws}Request2"));
}
@Endpoint
private static class MyEndpoint {
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
public void doIt() {
}
}
private static class OtherBean {
@PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws")
public void doIt() {
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
@Endpoint
class PayloadRootEndpoint {
@PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
public void doIt() {
}
}

View File

@@ -16,8 +16,11 @@
package org.springframework.ws.soap.server.endpoint.mapping;
import java.lang.reflect.Method;
import junit.framework.TestCase;
import static org.easymock.EasyMock.*;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.context.DefaultMessageContext;
@@ -43,18 +46,19 @@ public class SoapActionAnnotationMethodEndpointMappingTest extends TestCase {
}
public void testRegistration() throws Exception {
SoapMessage requestMock = createMock(SoapMessage.class);
expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction");
WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class);
replay(requestMock, factoryMock);
SoapMessage requestMock = createMock(SoapMessage.class);
expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction");
WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class);
replay(requestMock, factoryMock);
MessageContext context = new DefaultMessageContext(requestMock, factoryMock);
MessageContext context = new DefaultMessageContext(requestMock, factoryMock);
EndpointInvocationChain chain = mapping.getEndpoint(context);
assertNotNull("MethodEndpoint not registered", chain);
MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
Method doIt = MyEndpoint.class.getMethod("doIt", new Class[0]);
MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doIt);
assertEquals("Invalid endpoint registered", expected, chain.getEndpoint());
verify(requestMock,factoryMock);
verify(requestMock, factoryMock);
}
@Endpoint

View File

@@ -19,6 +19,7 @@ package org.springframework.ws.server.endpoint;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.JdkVersion;
import org.springframework.util.Assert;
@@ -32,9 +33,11 @@ import org.springframework.util.Assert;
*/
public final class MethodEndpoint {
private Object bean;
private final Object bean;
private Method method;
private final Method method;
private final BeanFactory beanFactory;
/**
* Constructs a new method endpoint with the given bean and method.
@@ -47,6 +50,7 @@ public final class MethodEndpoint {
Assert.notNull(method, "method must not be null");
this.bean = bean;
this.method = method;
this.beanFactory = null;
}
/**
@@ -62,6 +66,26 @@ public final class MethodEndpoint {
Assert.notNull(methodName, "method must not be null");
this.bean = bean;
this.method = bean.getClass().getMethod(methodName, parameterTypes);
this.beanFactory = null;
}
/**
* Constructs a new method endpoint with the given bean name and method. The bean name will be lazily initized when
* {@link #invoke(Object[])} is called.
*
* @param beanName the bean name
* @param beanFactory the bean factory to use for bean initialization
* @param method the method
*/
public MethodEndpoint(String beanName, BeanFactory beanFactory, Method method) {
Assert.hasText(beanName, "'beanName' must not be null");
Assert.notNull(beanFactory, "'beanFactory' must not be null");
Assert.notNull(method, "'method' must not be null");
Assert.isTrue(beanFactory.containsBean(beanName),
"Bean factory [" + beanFactory + "] does not contain bean " + "with name [" + beanName + "]");
this.bean = beanName;
this.beanFactory = beanFactory;
this.method = method;
}
/** Returns the object bean for this method endpoint. */
@@ -82,8 +106,13 @@ public final class MethodEndpoint {
* @throws Exception when the method invocation results in an exception
*/
public Object invoke(Object[] args) throws Exception {
Object endpoint = bean;
if (endpoint instanceof String) {
String endpointName = (String) endpoint;
endpoint = beanFactory.getBean(endpointName);
}
try {
return this.method.invoke(this.bean, args);
return this.method.invoke(endpoint, args);
}
catch (InvocationTargetException ex) {
handleInvocationTargetException(ex);

View File

@@ -124,6 +124,29 @@ public abstract class AbstractMethodEndpointMapping extends AbstractEndpointMapp
}
}
/**
* Helper method that registers the methods of the given class. This method iterates over the methods of the class,
* and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
* using {@link #registerEndpoint(String,MethodEndpoint)}.
*
* @see #getLookupKeyForMethod(Method)
*/
protected void registerMethods(String beanName) {
Assert.hasText(beanName, "'beanName' must not be empty");
Class endpointClass = getApplicationContext().getType(beanName);
Method[] methods = endpointClass.getMethods();
for (int i = 0; i < methods.length; i++) {
if (JdkVersion.isAtLeastJava15() && methods[i].isSynthetic() ||
methods[i].getDeclaringClass().equals(Object.class)) {
continue;
}
String key = getLookupKeyForMethod(methods[i]);
if (StringUtils.hasLength(key)) {
registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), methods[i]));
}
}
}
/**
* Returns the the endpoint keys for the given method. Returns <code>null</code> if the method is not to be
* registered, which is the default.