diff --git a/sandbox/pom.xml b/sandbox/pom.xml index bc36483e..a0c7f6d4 100644 --- a/sandbox/pom.xml +++ b/sandbox/pom.xml @@ -56,6 +56,12 @@ org.springframework spring-jms + + org.springframework + spring-jmx + ${spring.version} + test + javax.xml.soap diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java index c1722bc2..707ba938 100644 --- a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java @@ -17,6 +17,7 @@ package org.springframework.ws.server.endpoint.adapter; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties; import javax.xml.namespace.QName; @@ -104,14 +105,24 @@ public class XPathParamAnnotationEndpointAdapter extends AbstractMethodEndpointA } protected void invokeInternal(MessageContext messageContext, MethodEndpoint methodEndpoint) throws Exception { - Element payloadElement = getMessagePayloadElement(messageContext.getRequest()); - Object[] args = getMethodArguments(payloadElement, methodEndpoint.getMethod()); - Object result = methodEndpoint.invoke(args); - if (result != null && result instanceof Source) { - Source responseSource = (Source) result; - WebServiceMessage response = messageContext.getResponse(); - Transformer transformer = createTransformer(); - transformer.transform(responseSource, response.getPayloadResult()); + try { + Element payloadElement = getMessagePayloadElement(messageContext.getRequest()); + Object[] args = getMethodArguments(payloadElement, methodEndpoint.getMethod()); + Object result = methodEndpoint.invoke(args); + if (result != null && result instanceof Source) { + Source responseSource = (Source) result; + WebServiceMessage response = messageContext.getResponse(); + Transformer transformer = createTransformer(); + transformer.transform(responseSource, response.getPayloadResult()); + } + } + catch (InvocationTargetException ex) { + if (ex.getTargetException() instanceof Exception) { + throw (Exception) ex.getTargetException(); + } + else { + throw ex; + } } } diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/package.html b/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/package.html deleted file mode 100644 index febadcb4..00000000 --- a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Provides miscellaneous endpoints EndpointAdapter implementations. - - diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/Endpoint.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/Endpoint.java new file mode 100644 index 00000000..9aecfbd1 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/Endpoint.java @@ -0,0 +1,41 @@ +/* + * Copyright 2007 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.ws.server.endpoint.mapping.AbstractAnnotationEndpointMapping; +import org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationEndpointMapping; + +/** + * Marks a class as an endpoint. + *

+ * Instances of this class are typically picked up by an {@link AbstractAnnotationEndpointMapping} implementation, such + * as {@link SoapActionAnnotationEndpointMapping}. + * + * @author Arjen Poutsma + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Endpoint { + +} diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRootQName.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRootQName.java new file mode 100644 index 00000000..e6c55b8d --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRootQName.java @@ -0,0 +1,30 @@ +/* + * Copyright 2007 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface PayloadRootQName { + + String value(); + +} diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationEndpointMapping.java new file mode 100644 index 00000000..54f2c3a8 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationEndpointMapping.java @@ -0,0 +1,50 @@ +/* + * Copyright 2007 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.annotation.Annotation; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +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. + *

+ * Each bean + * + * @author Arjen Poutsma + */ +public abstract class AbstractAnnotationEndpointMapping extends AbstractMethodEndpointMapping + implements BeanPostProcessor { + + public final Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + registerMethods(bean); + return bean; + } + + /** Returns the 'endpoint' annotation type. Default is {@link Endpoint}. */ + protected Class getEndpointAnnotationType() { + return Endpoint.class; + } + + public final Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + +} diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java new file mode 100644 index 00000000..74dda141 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java @@ -0,0 +1,150 @@ +/* + * Copyright 2007 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 java.util.HashMap; +import java.util.Map; + +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContextException; +import org.springframework.core.JdkVersion; +import org.springframework.util.StringUtils; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.MethodEndpoint; + +/** + * Abstract base class for {@link MethodEndpoint} mappings. + *

+ * Subclasses typically implement {@link org.springframework.beans.factory.config.BeanPostProcessor} to look for beans + * that qualify as enpoint. The methods of this bean are then registered under a specific key with {@link + * #registerEndpoint(String,MethodEndpoint)}. + * + * @author Arjen Poutsma + */ +public abstract class AbstractMethodEndpointMapping extends AbstractEndpointMapping { + + /** Keys are Strings, values are {@link MethodEndpoint}s. */ + private final Map endpointMap = new HashMap(); + + /** + * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete + * subclass. + * + * @return the looked up endpoint, or null + * @see #getLookupKeyForMessage(MessageContext) + */ + protected Object getEndpointInternal(MessageContext messageContext) throws Exception { + String key = getLookupKeyForMessage(messageContext); + if (!StringUtils.hasLength(key)) { + return null; + } + if (logger.isDebugEnabled()) { + logger.debug("Looking up endpoint for [" + key + "]"); + } + return lookupEndpoint(key); + } + + /** + * Returns the the endpoint keys for the given message context. + * + * @return the registration keys + */ + protected abstract String getLookupKeyForMessage(MessageContext messageContext) throws Exception; + + /** + * Looks up an endpoint instance for the given keys. All keys are tried in order. + * + * @param key key the beans are mapped to + * @return the associated endpoint instance, or null if not found + */ + protected MethodEndpoint lookupEndpoint(String key) { + return (MethodEndpoint) endpointMap.get(key); + } + + /** + * Register the given endpoint instance under the key. + * + * @param key the lookup key + * @param endpoint the method endpoint instance + * @throws BeansException if the endpoint could not be registered + */ + protected void registerEndpoint(String key, MethodEndpoint endpoint) throws BeansException { + Object mappedEndpoint = endpointMap.get(key); + if (mappedEndpoint != null) { + throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on registration key [" + key + + "]: there's already endpoint [" + mappedEndpoint + "] mapped"); + } + if (endpoint == null) { + throw new ApplicationContextException("Could not find endpoint for key [" + key + "]"); + } + endpointMap.put(key, endpoint); + if (logger.isDebugEnabled()) { + logger.debug("Mapped key [" + key + "] onto endpoint [" + endpoint + "]"); + } + } + + /** + * Helper method that registers the methods of the given bean. This method iterates over the methods of the bean, + * 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(Object endpoint) { + Method[] methods = getEndpointClass(endpoint).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(endpoint, methods[i])); + } + } + } + + /** + * Returns the the endpoint keys for the given method. Returns null if the method is not to be + * registered, which is the default. + * + * @param method the method + * @return a registration key, or null if the method is not to be registered + */ + protected String getLookupKeyForMethod(Method method) { + return null; + } + + /** + * Return the class or interface to use for method reflection. + *

+ * Default implementation returns the target class for a CGLIB proxy, and the class of the given bean else (for a + * JDK proxy or a plain bean class). + * + * @param endpoint the bean instance (might be an AOP proxy) + * @return the bean class to expose + */ + protected Class getEndpointClass(Object endpoint) { + if (AopUtils.isCglibProxy(endpoint)) { + return endpoint.getClass().getSuperclass(); + } + return endpoint.getClass(); + } + +} diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameMethodEndpointMapping.java new file mode 100644 index 00000000..02e09601 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameMethodEndpointMapping.java @@ -0,0 +1,61 @@ +/* + * Copyright 2007 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.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.annotation.PayloadRootQName; +import org.springframework.xml.namespace.QNameUtils; +import org.w3c.dom.Element; + +/** @author Arjen Poutsma */ +public class PayloadRootQNameMethodEndpointMapping extends AbstractAnnotationEndpointMapping { + + private static TransformerFactory transformerFactory; + + static { + transformerFactory = TransformerFactory.newInstance(); + } + + protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception { + Element payloadElement = getMessagePayloadElement(messageContext.getRequest()); + QName qName = QNameUtils.getQNameForNode(payloadElement); + return qName != null ? qName.toString() : null; + } + + protected String getLookupKeyForMethod(Method method) { + PayloadRootQName annotation = AnnotationUtils.getAnnotation(method, PayloadRootQName.class); + return annotation != null ? annotation.value() : null; + } + + private Element getMessagePayloadElement(WebServiceMessage message) throws TransformerException { + Transformer transformer = transformerFactory.newTransformer(); + DOMResult domResult = new DOMResult(); + transformer.transform(message.getPayloadSource(), domResult); + return (Element) domResult.getNode().getFirstChild(); + } + + +} diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java new file mode 100644 index 00000000..18fac8dc --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java @@ -0,0 +1,116 @@ +/* + * Copyright 2007 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.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.MessageContext; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Simple subclass of {@link AbstractMethodEndpointMapping} that maps to all methods that start with a prefix, and end + * with a suffix. Endpoint beans are registered using the endpoints property. + * + * @author Arjen Poutsma + */ +public class SimpleMethodEndpointMapping extends AbstractMethodEndpointMapping implements InitializingBean { + + public static final String DEFAULT_METHOD_PREFIX = "handle"; + + public static final String DEFAULT_METHOD_SUFFIX = ""; + + private Object[] endpoints; + + private String methodPrefix = DEFAULT_METHOD_PREFIX; + + private String methodSuffix = DEFAULT_METHOD_SUFFIX; + + private TransformerFactory transformerFactory; + + /** Sets the endpoints */ + public void setEndpoints(Object[] endpoints) { + this.endpoints = endpoints; + } + + /** + * Sets the method prefix. All methods with names starting with this string will be registered. Default is + * "handle". + * + * @see #DEFAULT_METHOD_PREFIX + */ + public void setMethodPrefix(String methodPrefix) { + this.methodPrefix = methodPrefix; + } + + /** + * Sets the method suffix. All methods with names ending with this string will be registered. Default is "" (i.e. no + * suffix). + * + * @see #DEFAULT_METHOD_SUFFIX + */ + public void setMethodSuffix(String methodSuffix) { + this.methodSuffix = methodSuffix; + } + + public void afterPropertiesSet() throws Exception { + Assert.notEmpty(endpoints, "endpoints is required"); + transformerFactory = TransformerFactory.newInstance(); + for (int i = 0; i < endpoints.length; i++) { + registerMethods(endpoints[i]); + } + } + + /** Returns the name of the given method, with the prefix and suffix stripped off. */ + protected String getLookupKeyForMethod(Method method) { + String methodName = method.getName(); + if (methodName.startsWith(methodPrefix) && methodName.endsWith(methodSuffix)) { + return methodName.substring(methodPrefix.length(), methodName.length() - methodSuffix.length()); + } + else { + return null; + } + } + + protected String getLookupKeyForMessage(MessageContext messageContext) throws TransformerException { + Element payloadElement = getMessagePayloadElement(messageContext.getRequest()); + return payloadElement.getLocalName(); + } + + private Element getMessagePayloadElement(WebServiceMessage message) throws TransformerException { + if (message.getPayloadSource() instanceof DOMSource) { + DOMSource domSource = (DOMSource) message.getPayloadSource(); + if (domSource.getNode().getNodeType() == Node.ELEMENT_NODE) { + return (Element) domSource.getNode(); + } + } + Transformer transformer = transformerFactory.newTransformer(); + DOMResult domResult = new DOMResult(); + transformer.transform(message.getPayloadSource(), domResult); + return (Element) domResult.getNode().getFirstChild(); + } + + +} diff --git a/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java new file mode 100644 index 00000000..3857ce45 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java @@ -0,0 +1,31 @@ +/* + * Copyright 2007 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.soap.server.endpoint.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** @author Arjen Poutsma */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SoapAction { + + String value(); + +} diff --git a/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMapping.java new file mode 100644 index 00000000..0fc27a53 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMapping.java @@ -0,0 +1,107 @@ +/* + * Copyright 2007 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.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; +import org.springframework.ws.server.EndpointInterceptor; +import org.springframework.ws.server.EndpointInvocationChain; +import org.springframework.ws.server.endpoint.mapping.AbstractAnnotationEndpointMapping; +import org.springframework.ws.soap.SoapEndpointMapping; +import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.server.SoapEndpointInvocationChain; +import org.springframework.ws.soap.server.endpoint.annotation.SoapAction; + +/** + * Implementation of the EndpointMapping interface to map from SOAPAction headers to endpoint + * beans. Supports both mapping to bean instances and mapping to bean names: the latter is required for prototype + * handlers. + *

+ * The endpointMap property is suitable for populating the endpoint map with bean references, e.g. via the + * map element in XML bean definitions. + *

+ * Mappings to bean names can be set via the mappings property, in a form accepted by the + * java.util.Properties class, like as follows: + *

+ * http://www.springframework.org/spring-ws/samples/airline/BookFlight=bookFlightEndpoint
+ * http://www.springframework.org/spring-ws/samples/airline/GetFlights=getFlightsEndpoint
+ * 
+ * The syntax is SOAP_ACTION=ENDPOINT_BEAN_NAME. + *

+ * This endpoint mapping does not read from the request message, and therefore is more suitable for message contexts + * which directly read from the transport request (such as the AxiomSoapMessageContextFactory with the + * payloadCaching disabled). + * + * @author Arjen Poutsma + */ +public class SoapActionAnnotationEndpointMapping extends AbstractAnnotationEndpointMapping + implements SoapEndpointMapping { + + private String[] actorsOrRoles; + + public final void setActorOrRole(String actorOrRole) { + Assert.notNull(actorOrRole, "actorOrRole must not be null"); + actorsOrRoles = new String[]{actorOrRole}; + } + + public final void setActorsOrRoles(String[] actorsOrRoles) { + Assert.notEmpty(actorsOrRoles, "actorsOrRoles must not be empty"); + this.actorsOrRoles = actorsOrRoles; + } + + /** + * Creates a new SoapEndpointInvocationChain based on the given endpoint, and the set interceptors, and + * actors/roles. + * + * @param endpoint the endpoint + * @param interceptors the endpoint interceptors + * @return the created invocation chain + * @see #setInterceptors(org.springframework.ws.server.EndpointInterceptor[]) + * @see #setActorsOrRoles(String[]) + */ + protected final EndpointInvocationChain createEndpointInvocationChain(MessageContext messageContext, + Object endpoint, + EndpointInterceptor[] interceptors) { + return new SoapEndpointInvocationChain(endpoint, interceptors, actorsOrRoles); + } + + protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception { + if (messageContext.getRequest() instanceof SoapMessage) { + SoapMessage request = (SoapMessage) messageContext.getRequest(); + String soapAction = request.getSoapAction(); + if (StringUtils.hasLength(soapAction) && soapAction.charAt(0) == '"' && + soapAction.charAt(soapAction.length() - 1) == '"') { + return soapAction.substring(1, soapAction.length() - 1); + } + else { + return soapAction; + } + } + else { + return null; + } + } + + protected String getLookupKeyForMethod(Method method) { + SoapAction soapAction = AnnotationUtils.getAnnotation(method, SoapAction.class); + return soapAction != null ? soapAction.value() : null; + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java index 7cb8f8e1..02f7fbc4 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java @@ -17,6 +17,7 @@ package org.springframework.ws.transport.jms; import java.io.IOException; +import javax.jms.BytesMessage; import javax.jms.JMSException; import javax.jms.Queue; import javax.jms.QueueConnection; @@ -30,7 +31,15 @@ import org.springframework.util.Assert; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageSender; -/** @author Arjen Poutsma */ +/** + * WebServiceMessageSender implementation that uses JMS {@link Queue}. + *

+ * This message sender sends the request message of the queue configured with either the queue or + * queueName property. It creates a temporary queue for the response message. For both request and response + * {@link BytesMessage}s are used. + * + * @author Arjen Poutsma + */ public class JmsMessageSender implements WebServiceMessageSender { /** Default timeout for receive operations. */ diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java index f08bc295..77c39fea 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java @@ -30,7 +30,8 @@ import org.springframework.ws.transport.WebServiceMessageReceiver; * Spring-2.0 {@link SessionAwareMessageListener} that can be used to handleMessage incoming JMS messages. Requires a * {@link WebServiceMessageFactory} which is used to convert the incoming JMS {@link BytesMessage}s into a {@link * WebServiceMessage}, and passes that context to the {@link WebServiceMessageReceiver} set with the property - * messageReceiver. If a response is created, it is sent using a response JMS message. + * messageReceiver. If a response is created, it is sent using the {@link BytesMessage#getJMSReplyTo() + * reply to header} of the request message. * * @author Arjen Poutsma * @see #setMessageFactory(org.springframework.ws.WebServiceMessageFactory) diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java new file mode 100644 index 00000000..412455ec --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2007 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.transport.mail; + +import javax.jms.JMSException; +import javax.mail.MessagingException; + +import org.springframework.ws.transport.TransportException; + +/** @author Arjen Poutsma */ +public class MailTransportException extends TransportException { + + public MailTransportException(String msg) { + super(msg); + } + + public MailTransportException(String msg, JMSException ex) { + super(msg + ": " + ex.getMessage()); + } + + public MailTransportException(MessagingException ex) { + super(ex.getMessage()); + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java index 07a5468e..c72c26a2 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java @@ -23,9 +23,7 @@ import javax.mail.MessagingException; import org.springframework.ws.transport.TransportOutputStream; -/** - * @author Arjen Poutsma - */ +/** @author Arjen Poutsma */ public class MailTransportOutputStream extends TransportOutputStream { private final Message message; @@ -51,4 +49,5 @@ public class MailTransportOutputStream extends TransportOutputStream { throw new IOException(ex.getMessage()); } } + } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java new file mode 100644 index 00000000..557131f6 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java @@ -0,0 +1,171 @@ +/* + * Copyright 2007 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.transport.support; + +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.Lifecycle; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; +import org.springframework.util.ClassUtils; + +/** + * Abstract base class for standalone, server-side transport objects. Contains a Spring {@link TaskExecutor}, and + * various lifecycle callbacks. + * + * @author Arjen Poutsma + */ +public abstract class AbstractMessagingContainer extends SimpleWebServiceMessageReceiverObjectSupport + implements Lifecycle, DisposableBean, BeanNameAware { + + /** Default thread name prefix. */ + public final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils.getShortName(getClass()) + "-"; + + private volatile boolean active = false; + + private boolean autoStartup = true; + + private boolean running = false; + + private final Object lifecycleMonitor = new Object(); + + private TaskExecutor taskExecutor; + + private String beanName; + + /** + * Set whether to automatically start the listener after initialization. + *

+ * Default is true; set this to false to allow for manual startup. + */ + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + /** + * Set the Spring {@link TaskExecutor} to use for running the listener threads. Default is {@link + * SimpleAsyncTaskExecutor}, starting up a number of new threads. + *

+ * Specify an alternative task executor for integration with an existing thread pool, such as the {@link + * org.springframework.scheduling.commonj.WorkManagerTaskExecutor} to integrate with WebSphere or WebLogic. + */ + public void setTaskExecutor(TaskExecutor taskExecutor) { + this.taskExecutor = taskExecutor; + } + + /** Returns the task executor. */ + public TaskExecutor getTaskExecutor() { + return taskExecutor; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + /** Return whether this server is currently active, that is, whether it has been set up but not shut down yet. */ + public final boolean isActive() { + synchronized (lifecycleMonitor) { + return active; + } + } + + /** Return whether this server is currently running, that is, whether it has been started and not stopped yet. */ + public final boolean isRunning() { + synchronized (lifecycleMonitor) { + return running; + } + } + + /** + * Create a default TaskExecutor. Called if no explicit TaskExecutor has been specified. + *

+ * The default implementation builds a {@link org.springframework.core.task.SimpleAsyncTaskExecutor} with the + * specified bean name (or the class name, if no bean name specified) as thread name prefix. + * + * @see org.springframework.core.task.SimpleAsyncTaskExecutor#SimpleAsyncTaskExecutor(String) + */ + protected TaskExecutor createDefaultTaskExecutor() { + String threadNamePrefix = beanName != null ? beanName + "-" : DEFAULT_THREAD_NAME_PREFIX; + return new SimpleAsyncTaskExecutor(threadNamePrefix); + } + + public void afterPropertiesSet() throws Exception { + super.afterPropertiesSet(); + if (taskExecutor == null) { + taskExecutor = createDefaultTaskExecutor(); + } + activate(); + } + + /** + * Calls shutdown when the BeanFactory destroys the server instance. + * + * @see #shutdown() + */ + public void destroy() { + shutdown(); + } + + /** Initialize this server. Starts the server if autoStartup hasn't been turned off. */ + public final void activate() throws Exception { + synchronized (lifecycleMonitor) { + active = true; + lifecycleMonitor.notifyAll(); + } + onActivate(); + if (autoStartup) { + start(); + } + } + + /** Start this server. */ + public final void start() { + synchronized (lifecycleMonitor) { + running = true; + lifecycleMonitor.notifyAll(); + } + onStart(); + } + + /** Stop this server. */ + public final void stop() { + synchronized (lifecycleMonitor) { + running = false; + lifecycleMonitor.notifyAll(); + } + onStop(); + } + + /** Shut down the registered listeners and close this listener container. */ + public final void shutdown() { + synchronized (lifecycleMonitor) { + running = false; + active = false; + lifecycleMonitor.notifyAll(); + } + onShutdown(); + } + + protected abstract void onActivate() throws Exception; + + protected abstract void onStart(); + + protected abstract void onStop(); + + protected abstract void onShutdown(); + +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java new file mode 100644 index 00000000..637dc5a2 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java @@ -0,0 +1,73 @@ +/* + * Copyright 2007 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.transport.tcp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; +import org.springframework.ws.transport.WebServiceConnection; +import org.springframework.ws.transport.WebServiceMessageSender; + +/** @author Arjen Poutsma */ +public class TcpMessageSender implements WebServiceMessageSender, InitializingBean { + + private InetAddress address; + + private int port = -1; + + private int timeOut = 1000; + + /** Sets the port the sender will connect to. */ + public void setPort(int port) { + this.port = port; + } + + /** Sets the amount of milliseconds before the tcp connection will timeout. */ + public void setTimeOut(int timeOut) { + this.timeOut = timeOut; + } + + /** + * Sets the internet address the client will connect to. + * + * @throws java.net.UnknownHostException when the given address is not known + */ + public void setAddress(String address) throws UnknownHostException { + this.address = InetAddress.getByName(address); + } + + public WebServiceConnection createConnection() throws IOException { + Socket socket = new Socket(); + SocketAddress socketAddress = new InetSocketAddress(address, port); + socket.connect(socketAddress, timeOut); + return new TcpSendingWebServiceConnection(socket); + } + + public void afterPropertiesSet() throws Exception { + if (port == -1) { + throw new IllegalArgumentException("port is required"); + } + Assert.notNull(address, "address is required"); + + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java new file mode 100644 index 00000000..9f05db5d --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java @@ -0,0 +1,167 @@ +/* + * Copyright 2007 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.transport.tcp; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.springframework.ws.transport.WebServiceConnection; +import org.springframework.ws.transport.support.AbstractMessagingContainer; + +/** @author Arjen Poutsma */ +public class TcpMessagingContainer extends AbstractMessagingContainer { + + private ServerSocket serverSocket; + + private InetAddress bindAddress; + + private int backlog = -1; + + private int port = -1; + + /** Sets the port the server will bind to. */ + public void setPort(int port) { + this.port = port; + } + + /** Sets the server back log. */ + public void setBacklog(int backlog) { + this.backlog = backlog; + } + + /** + * Sets the local internet address the server will bind to. By default, it will accept connections on any/all local + * addresses. + * + * @throws java.net.UnknownHostException when the given address is not known + * @see java.net.ServerSocket#ServerSocket(int,int,java.net.InetAddress) + */ + public void setBindAddress(String bindAddress) throws UnknownHostException { + this.bindAddress = InetAddress.getByName(bindAddress); + } + + public void afterPropertiesSet() throws Exception { + super.afterPropertiesSet(); + if (port == -1) { + throw new IllegalArgumentException("port is required"); + } + openServerSocket(); + } + + protected void onActivate() throws IOException { + openServerSocket(); + } + + protected void onStart() { + if (logger.isInfoEnabled()) { + logger.info("Starting tcp server [" + serverSocket.getLocalSocketAddress() + "]"); + } + getTaskExecutor().execute(new SocketAcceptingRunnable()); + } + + protected void onStop() { + if (logger.isInfoEnabled()) { + logger.info("Stopping tcp server [" + serverSocket.getLocalSocketAddress() + "]"); + } + } + + protected void onShutdown() { + if (logger.isInfoEnabled()) { + logger.info("Shutting down tcp server [" + serverSocket.getLocalSocketAddress() + "]"); + } + closeServerSocket(); + } + + /** + * Establish a shared ServerSocket for this server. + *

+ * The default implementation delegates to refreshSharedConnection, which does one immediate attempt + * and throws an exception if it fails. Can be overridden to have a recovery proces in place, retrying until a + * ServerSocket can be successfully established. + * + * @see #refreshServerSocket() + */ + protected void openServerSocket() throws IOException { + refreshServerSocket(); + } + + /** + * Refresh the shared ServerSocket that this server holds. + *

+ * Called on startup and also after an infrastructure exception that occured during listener setup and/or + * execution. + */ + protected final void refreshServerSocket() throws IOException { + closeServerSocket(); + serverSocket = new ServerSocket(port, backlog, bindAddress); + } + + protected void closeServerSocket() { + if (serverSocket == null) { + return; + } + try { + serverSocket.close(); + } + catch (IOException ex) { + logger.debug("Could not close ServerSocket", ex); + } + } + + private class SocketAcceptingRunnable implements Runnable { + + public void run() { + while (isRunning()) { + try { + Socket socket = serverSocket.accept(); + TcpRequestHandler handler = new TcpRequestHandler(socket); + getTaskExecutor().execute(handler); + } + catch (InterruptedIOException ex) { + logger.warn(ex); + } + catch (IOException ex) { + logger.warn("Could not accept incoming connection: " + ex.getMessage()); + } + } + } + } + + private class TcpRequestHandler implements Runnable { + + private final Socket socket; + + public TcpRequestHandler(Socket socket) { + this.socket = socket; + } + + public void run() { + WebServiceConnection connection = new TcpReceivingWebServiceConnection(socket); + try { + handleConnection(connection); + } + catch (Exception ex) { + logger.warn("Could not handle request", ex); + } + } + } + +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java new file mode 100644 index 00000000..b57babe7 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java @@ -0,0 +1,80 @@ +/* + * Copyright 2007 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.transport.tcp; + +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Collections; +import java.util.Iterator; + +import org.springframework.util.Assert; +import org.springframework.ws.transport.AbstractReceivingWebServiceConnection; + +/** @author Arjen Poutsma */ +public class TcpReceivingWebServiceConnection extends AbstractReceivingWebServiceConnection { + + private final Socket socket; + + public TcpReceivingWebServiceConnection(Socket socket) { + Assert.notNull(socket, "socket must not be null"); + this.socket = socket; + } + + public void close() throws IOException { + socket.close(); + } + + protected Iterator getRequestHeaderNames() throws IOException { + return Collections.EMPTY_LIST.iterator(); + } + + protected Iterator getRequestHeaders(String name) throws IOException { + return Collections.EMPTY_LIST.iterator(); + } + + protected InputStream getRequestInputStream() throws IOException { + return new FilterInputStream(socket.getInputStream()) { + + public void close() throws IOException { + // don't close the socket + socket.shutdownInput(); + } + }; + } + + protected void addResponseHeader(String name, String value) throws IOException { + } + + protected OutputStream getResponseOutputStream() throws IOException { + return new FilterOutputStream(socket.getOutputStream()) { + + public void close() throws IOException { + // don't close the socket + socket.shutdownOutput(); + } + }; + } + + protected void sendResponse() throws IOException { + } + + +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java new file mode 100644 index 00000000..005ec35c --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java @@ -0,0 +1,82 @@ +/* + * Copyright 2007 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.transport.tcp; + +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Collections; +import java.util.Iterator; + +import org.springframework.util.Assert; +import org.springframework.ws.transport.AbstractSendingWebServiceConnection; + +/** @author Arjen Poutsma */ +public class TcpSendingWebServiceConnection extends AbstractSendingWebServiceConnection { + + private final Socket socket; + + public TcpSendingWebServiceConnection(Socket socket) { + Assert.notNull(socket, "socket must not be null"); + this.socket = socket; + } + + public void close() throws IOException { + socket.close(); + } + + protected void addRequestHeader(String name, String value) throws IOException { + } + + protected OutputStream getRequestOutputStream() throws IOException { + return new FilterOutputStream(socket.getOutputStream()) { + + public void close() throws IOException { + // don't close the socket + socket.shutdownOutput(); + } + }; + } + + protected void sendRequest() throws IOException { + } + + protected boolean hasResponse() throws IOException { + return true; + } + + protected Iterator getResponseHeaderNames() throws IOException { + return Collections.EMPTY_LIST.iterator(); + } + + protected Iterator getResponseHeaders(String name) throws IOException { + return Collections.EMPTY_LIST.iterator(); + } + + protected InputStream getResponseInputStream() throws IOException { + return new FilterInputStream(socket.getInputStream()) { + + public void close() throws IOException { + // don't close the socket + socket.shutdownInput(); + } + }; + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpTransportException.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpTransportException.java new file mode 100644 index 00000000..a1797367 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpTransportException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2007 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.transport.tcp; + +import java.io.IOException; + +import org.springframework.ws.transport.TransportException; + +/** @author Arjen Poutsma */ +public class TcpTransportException extends TransportException { + + public TcpTransportException(String msg) { + super(msg); + } + + public TcpTransportException(String msg, IOException ex) { + super(msg + ": " + ex.getMessage()); + } + + public TcpTransportException(IOException ex) { + super(ex.getMessage()); + } +} diff --git a/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/AnnotationEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/AnnotationEndpointMappingTest.java new file mode 100644 index 00000000..934210d3 --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/AnnotationEndpointMappingTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2007 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.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; + +import junit.framework.TestCase; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.MethodEndpoint; +import org.springframework.ws.server.endpoint.annotation.Endpoint; + +/** TestCase for {@link AbstractAnnotationEndpointMapping} */ +public class AnnotationEndpointMappingTest extends TestCase { + + private AbstractAnnotationEndpointMapping mapping; + + private StaticApplicationContext applicationContext; + + protected void setUp() throws Exception { + applicationContext = new StaticApplicationContext(); + applicationContext.registerSingleton("mapping", MyAnnotationEndpointMapping.class); + applicationContext.registerSingleton("endpoint", MyEndpoint.class); + applicationContext.registerSingleton("other", OtherBean.class); + applicationContext.refresh(); + mapping = (AbstractAnnotationEndpointMapping) applicationContext.getBean("mapping"); + } + + public void testRegistration() throws NoSuchMethodException { + MethodEndpoint endpoint = mapping.lookupEndpoint("arg"); + assertNotNull("MethodEndpoint not registered", endpoint); + MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]); + assertEquals("Invalid endpoint registered", expected, endpoint); + + assertNull("Invalid endpoint registered", mapping.lookupEndpoint("arg2")); + } + + private static class MyAnnotationEndpointMapping extends AbstractAnnotationEndpointMapping { + + protected String getLookupKeyForMethod(Method method) { + MyAnnotation annotation = AnnotationUtils.getAnnotation(method, MyAnnotation.class); + if (annotation != null) { + return annotation.value(); + } + else { + return null; + } + } + + protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception { + return "arg"; + } + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface MyAnnotation { + + String value(); + + } + + @Endpoint + private static class MyEndpoint { + + @MyAnnotation("arg") + public void doIt() { + + } + + } + + private static class OtherBean { + + @MyAnnotation("arg2") + public void doIt() { + + } + + } + +} \ No newline at end of file diff --git a/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMappingTest.java new file mode 100644 index 00000000..b029b4ba --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMappingTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2007 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 junit.framework.TestCase; +import org.springframework.ws.MockWebServiceMessage; +import org.springframework.ws.MockWebServiceMessageFactory; +import org.springframework.ws.context.DefaultMessageContext; +import org.springframework.ws.context.MessageContext; + +public class SimpleMethodEndpointMappingTest extends TestCase { + + private SimpleMethodEndpointMapping mapping; + + protected void setUp() throws Exception { + mapping = new SimpleMethodEndpointMapping(); + mapping.setMethodPrefix("prefix"); + mapping.setMethodSuffix("Suffix"); + MyBean bean = new MyBean(); + mapping.setEndpoints(new Object[]{bean}); + mapping.afterPropertiesSet(); + } + + public void testRegistration() throws Exception { + assertNotNull("Endpoint not registered", mapping.lookupEndpoint("MyRequest")); + } + + public void testGetLookupKeyForMessageNoNamespace() throws Exception { + MockWebServiceMessage request = new MockWebServiceMessage(""); + MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory()); + String result = mapping.getLookupKeyForMessage(messageContext); + assertEquals("Invalid lookup key", "MyRequest", result); + } + + public void testGetLookupKeyForMessageNamespace() throws Exception { + MockWebServiceMessage request = + new MockWebServiceMessage(""); + MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory()); + String result = mapping.getLookupKeyForMessage(messageContext); + assertEquals("Invalid lookup key", "MyRequest", result); + } + + private static class MyBean { + + public void prefixMyRequestSuffix() { + + } + + public void request() { + + } + } +} \ No newline at end of file diff --git a/sandbox/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMappingTest.java new file mode 100644 index 00000000..806baf17 --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMappingTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2007 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.soap.server.endpoint.mapping; + +import junit.framework.TestCase; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.ws.server.endpoint.annotation.Endpoint; +import org.springframework.ws.soap.server.endpoint.annotation.SoapAction; + +public class SoapActionAnnotationEndpointMappingTest extends TestCase { + + private StaticApplicationContext applicationContext; + + private SoapActionAnnotationEndpointMapping mapping; + + protected void setUp() throws Exception { + applicationContext = new StaticApplicationContext(); + applicationContext.registerSingleton("mapping", SoapActionAnnotationEndpointMapping.class); + applicationContext.registerSingleton("endpoint", MyEndpoint.class); + applicationContext.refresh(); + mapping = (SoapActionAnnotationEndpointMapping) applicationContext.getBean("mapping"); + } + + public void testIt() { + + } + + @Endpoint + private static class MyEndpoint { + + @SoapAction("http://springframework.org/spring-ws/action") + public void handleMessage() { + + } + } +} \ No newline at end of file diff --git a/sandbox/src/test/java/org/springframework/ws/transport/jms/SimpleTestingMessageReceiver.java b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java similarity index 91% rename from sandbox/src/test/java/org/springframework/ws/transport/jms/SimpleTestingMessageReceiver.java rename to sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java index 7597d253..b40d0948 100644 --- a/sandbox/src/test/java/org/springframework/ws/transport/jms/SimpleTestingMessageReceiver.java +++ b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package org.springframework.ws.transport.jms; +package org.springframework.ws.transport; import javax.xml.transform.Transformer; import junit.framework.Assert; import org.springframework.ws.context.MessageContext; -import org.springframework.ws.transport.WebServiceMessageReceiver; import org.springframework.xml.transform.TransformerObjectSupport; public class SimpleTestingMessageReceiver extends TransformerObjectSupport implements WebServiceMessageReceiver { public void receive(MessageContext messageContext) throws Exception { Assert.assertNotNull("MessageContext is null", messageContext); + logger.info("Received message"); Transformer transformer = createTransformer(); transformer.transform(messageContext.getRequest().getPayloadSource(), messageContext.getResponse().getPayloadResult()); diff --git a/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java b/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java new file mode 100644 index 00000000..9e957a04 --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2007 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.transport.tcp; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.Socket; +import javax.xml.transform.stream.StreamResult; + +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.springframework.ws.WebServiceMessageFactory; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.xml.transform.StringSource; + +public class TcpServerIntegrationTest extends AbstractDependencyInjectionSpringContextTests { + + private WebServiceMessageFactory messageFactory; + + private WebServiceMessageSender messageSender; + + public void setMessageFactory(WebServiceMessageFactory messageFactory) { + this.messageFactory = messageFactory; + } + + public void setMessageSender(WebServiceMessageSender messageSender) { + this.messageSender = messageSender; + } + + public static final String REQUEST = + "\n" + + " \n" + + " \n" + + " DIS\n" + " \n" + + " \n" + ""; + + public void testServer() throws IOException, InterruptedException { + Socket socket = new Socket("localhost", 9999); + Writer writer = null; + BufferedReader reader = null; + try { + writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); + writer.write(REQUEST); + writer.flush(); + socket.shutdownOutput(); + reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); + String line = null; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } + finally { + socket.close(); + } + } + + public void testTemplate() throws Exception { + WebServiceTemplate template = new WebServiceTemplate(messageFactory, messageSender); + template.sendAndReceive(new StringSource(REQUEST), new StreamResult(System.out)); + } + + protected String[] getConfigLocations() { + return new String[]{"classpath:/org/springframework/ws/transport/tcp/applicationContext.xml"}; + } + +} \ No newline at end of file diff --git a/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml b/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml index e0ee3660..b65ff699 100644 --- a/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml +++ b/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml @@ -28,7 +28,7 @@ - + \ No newline at end of file diff --git a/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml b/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml new file mode 100644 index 00000000..449cb071 --- /dev/null +++ b/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.context.Lifecycle + + + + + + + +