diff --git a/sandbox/pom.xml b/sandbox/pom.xml index 8977b19b..05f0af25 100644 --- a/sandbox/pom.xml +++ b/sandbox/pom.xml @@ -1,4 +1,5 @@ - spring-ws @@ -21,6 +22,16 @@ + + + + maven-javadoc-plugin + + ${basedir}/../src/main/javadoc/javadoc.css + + + + @@ -56,6 +67,11 @@ org.springframework spring-jms + + org.springframework + spring-remoting + ${spring.version} + org.springframework spring-jmx @@ -84,10 +100,10 @@ provided - javax.jbi - jbi - 1.0 - provided + javax.ejb + ejb + 2.1 + true javax.xml.ws @@ -127,5 +143,12 @@ 6.0.1 test + + + easymock + easymock + 1.2_Java1.3 + test + diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsReceiverConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsReceiverConnection.java index 46a083ab..b6f82057 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsReceiverConnection.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsReceiverConnection.java @@ -32,11 +32,15 @@ import javax.jms.Session; import org.apache.commons.logging.Log; import org.springframework.jms.support.JmsUtils; import org.springframework.util.Assert; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.transport.AbstractReceiverConnection; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.jms.support.JmsTransportUtils; /** @author Arjen Poutsma */ -public class JmsReceiverConnection extends AbstractReceiverConnection implements JmsTransportConstants { +public class JmsReceiverConnection extends AbstractReceiverConnection + implements JmsTransportConstants, FaultAwareWebServiceConnection { private final Log logger; @@ -63,9 +67,6 @@ public class JmsReceiverConnection extends AbstractReceiverConnection implements return false; } - public void close() throws IOException { - } - /* * Receiving */ @@ -102,15 +103,18 @@ public class JmsReceiverConnection extends AbstractReceiverConnection implements } /* - * Sending - */ + * Sending + */ protected void onSendBeforeWrite(WebServiceMessage message) throws IOException { try { responseMessage = session.createBytesMessage(); responseMessage.setJMSCorrelationID(requestMessage.getJMSMessageID()); responseMessage.setStringProperty(PROPERTY_BINDING_VERSION, "1.0"); - responseMessage.setBooleanProperty(PROPERTY_IS_FAULT, message.hasFault()); + if (message instanceof FaultAwareWebServiceMessage) { + FaultAwareWebServiceMessage faultMessage = (FaultAwareWebServiceMessage) message; + responseMessage.setBooleanProperty(PROPERTY_IS_FAULT, faultMessage.hasFault()); + } } catch (JMSException ex) { throw new JmsTransportException("Could not create response message", ex); @@ -152,4 +156,32 @@ public class JmsReceiverConnection extends AbstractReceiverConnection implements } } + public void close() throws IOException { + } + + /* + * Faults + */ + + public boolean hasFault() throws IOException { + try { + return requestMessage.getBooleanProperty(JmsTransportConstants.PROPERTY_IS_FAULT); + } + catch (JMSException ex) { + throw new JmsTransportException(ex); + } + } + + public void setFault(boolean fault) throws IOException { + if (responseMessage != null) { + try { + responseMessage.setBooleanProperty(JmsTransportConstants.PROPERTY_IS_FAULT, true); + } + catch (JMSException ex) { + throw new JmsTransportException(ex); + } + } + } + + } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsSenderConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsSenderConnection.java index d269c35e..175e1e7d 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsSenderConnection.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsSenderConnection.java @@ -37,9 +37,11 @@ import javax.jms.TemporaryQueue; import org.springframework.jms.connection.ConnectionFactoryUtils; import org.springframework.jms.support.JmsUtils; import org.springframework.util.Assert; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.transport.AbstractSenderConnection; import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.jms.support.JmsTransportUtils; /** @author Arjen Poutsma */ public class JmsSenderConnection extends AbstractSenderConnection @@ -88,6 +90,14 @@ public class JmsSenderConnection extends AbstractSenderConnection return responseMessage; } + public boolean hasError() throws IOException { + return false; + } + + public String getErrorMessage() throws IOException { + return null; + } + /* * Sending */ @@ -96,8 +106,11 @@ public class JmsSenderConnection extends AbstractSenderConnection try { requestMessage = session.createBytesMessage(); requestMessage.setStringProperty(PROPERTY_BINDING_VERSION, "1.0"); - requestMessage.setBooleanProperty(PROPERTY_IS_FAULT, message.hasFault()); - requestMessage.setStringProperty(PROPERTY_REQUEST_IRI, uri.getUri()); + if (message instanceof FaultAwareWebServiceMessage) { + FaultAwareWebServiceMessage faultMessage = (FaultAwareWebServiceMessage) message; + requestMessage.setBooleanProperty(PROPERTY_IS_FAULT, faultMessage.hasFault()); + } + requestMessage.setStringProperty(PROPERTY_REQUEST_IRI, uri.toString()); } catch (JMSException ex) { throw new JmsTransportException(ex); @@ -176,6 +189,10 @@ public class JmsSenderConnection extends AbstractSenderConnection } } + protected boolean hasResponse() throws IOException { + return responseMessage != null; + } + protected Iterator getResponseHeaderNames() throws IOException { try { List headerNames = new ArrayList(); @@ -216,9 +233,9 @@ public class JmsSenderConnection extends AbstractSenderConnection ConnectionFactoryUtils.releaseConnection(connection, connectionFactory, true); } - protected boolean hasResponse() throws IOException { - return responseMessage != null; - } + /* + * Faults + */ public boolean hasFault() throws IOException { if (responseMessage != null) { @@ -234,11 +251,13 @@ public class JmsSenderConnection extends AbstractSenderConnection } } - public boolean hasError() throws IOException { - return false; + public void setFault(boolean fault) throws IOException { + try { + requestMessage.setBooleanProperty(JmsTransportConstants.PROPERTY_IS_FAULT, true); + } + catch (JMSException ex) { + throw new JmsTransportException(ex); + } } - public String getErrorMessage() throws IOException { - return null; - } } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsUri.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsUri.java index 7df5ec1e..199a9a4c 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsUri.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsUri.java @@ -28,49 +28,17 @@ import javax.naming.Context; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.ws.transport.support.ParameterizedUri; /** * @author Arjen Poutsma * @see RI * Scheme for Java Message Service 1.0 RC1 */ -public class JmsUri implements JmsTransportConstants { - - private final String uri; - - private final String destination; - - // keys are string parameter names; values are string parameter values - private final Map parameters = new HashMap(); +public class JmsUri extends ParameterizedUri implements JmsTransportConstants { public JmsUri(String uri) { - Assert.hasLength(uri, "'uri' must not be empty"); - String scheme = URI_SCHEME + ":"; - Assert.isTrue(uri.startsWith(scheme), uri + " does not start with " + scheme); - Assert.isTrue(uri.length() > scheme.length(), uri + " does not have a destination"); - int paramStart = uri.indexOf('?'); - if (paramStart == -1) { - destination = uri.substring(scheme.length()); - } - else { - destination = uri.substring(scheme.length(), paramStart); - parseParameters(uri.substring(paramStart + 1)); - } - this.uri = uri; - } - - private void parseParameters(String parametersString) { - StringTokenizer params = new StringTokenizer(parametersString, "&"); - while (params.hasMoreTokens()) { - String param = params.nextToken(); - int paramSep = param.indexOf('='); - if (paramSep == -1) { - throw new IllegalArgumentException(param + " is not a valid parameter: it has no '='"); - } - String paramName = param.substring(0, paramSep); - String paramValue = param.substring(paramSep + 1); - parameters.put(paramName, paramValue); - } + super(uri); validateParameters(); } @@ -85,7 +53,7 @@ public class JmsUri implements JmsTransportConstants { } private void validateIntegerParameter(String paramName) { - String paramValue = (String) parameters.get(paramName); + String paramValue = (String) getParameter(paramName); if (StringUtils.hasLength(paramValue)) { try { Integer.parseInt(paramValue); @@ -106,9 +74,8 @@ public class JmsUri implements JmsTransportConstants { return getIntegerParameter(PARAM_DELIVERY_MODE, Message.DEFAULT_DELIVERY_MODE); } - /** Returns thethe JNDI name of the destination queue or topic. */ public String getDestination() { - return destination; + return super.getDestination(); } /** @@ -116,7 +83,7 @@ public class JmsUri implements JmsTransportConstants { * "topic", respectively. */ public String getDestinationType() { - return (String) parameters.get(PARAM_DESTINATION_TYPE); + return getParameter(PARAM_DESTINATION_TYPE); } /** @@ -134,15 +101,10 @@ public class JmsUri implements JmsTransportConstants { } private int getIntegerParameter(String paramName, int defaultValue) { - String paramValue = (String) parameters.get(paramName); + String paramValue = getParameter(paramName); return paramValue != null ? Integer.parseInt(paramValue) : defaultValue; } - /** Returns the full JMS URI. */ - public String getUri() { - return uri; - } - /** Indicates whether this URI has a connection factory name. */ public boolean hasConnectionFactoryName() { return StringUtils.hasLength(getConnectionFactoryName()); @@ -150,7 +112,7 @@ public class JmsUri implements JmsTransportConstants { /** Returns the JNDI name of the Java class providing the connection factory. */ public String getConnectionFactoryName() { - return (String) parameters.get(PARAM_CONNECTION_FACTORY_NAME); + return getParameter(PARAM_CONNECTION_FACTORY_NAME); } /** Indicates whether this URI has a "InitialContextFactory". */ @@ -164,7 +126,7 @@ public class JmsUri implements JmsTransportConstants { * @see Context#INITIAL_CONTEXT_FACTORY */ public String getInitialContextFactory() { - return (String) parameters.get(PARAM_INITIAL_CONTEXT_FACTORY); + return getParameter(PARAM_INITIAL_CONTEXT_FACTORY); } /** Indicates whether this URI has a JNDI provider URL. */ @@ -178,7 +140,7 @@ public class JmsUri implements JmsTransportConstants { * @see Context#PROVIDER_URL */ public String getJndiUrl() { - return (String) parameters.get(PARAM_JNDI_URL); + return getParameter(PARAM_JNDI_URL); } /** Indicates whether this URI has a reply-to name. */ @@ -192,7 +154,7 @@ public class JmsUri implements JmsTransportConstants { * @see Message#setJMSReplyTo(Destination) */ public String getReplyTo() { - return (String) parameters.get(PARAM_REPLY_TO_NAME); + return getParameter(PARAM_REPLY_TO_NAME); } /** @@ -203,8 +165,4 @@ public class JmsUri implements JmsTransportConstants { return DESTINATION_TYPE_TOPIC.equals(getDestinationType()); } - /** Returns the value of a custom parameter with the given name. */ - public String getCustomParameter(String paramName) { - return (String) parameters.get(paramName); - } } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsTransportUtils.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/support/JmsTransportUtils.java similarity index 93% rename from sandbox/src/main/java/org/springframework/ws/transport/jms/JmsTransportUtils.java rename to sandbox/src/main/java/org/springframework/ws/transport/jms/support/JmsTransportUtils.java index 6eec7c4b..872a1fdb 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsTransportUtils.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/support/JmsTransportUtils.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package org.springframework.ws.transport.jms; +package org.springframework.ws.transport.jms.support; + +import org.springframework.ws.transport.jms.JmsTransportConstants; /** @author Arjen Poutsma */ public class JmsTransportUtils { diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailMessageReceiver.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailMessageReceiver.java new file mode 100644 index 00000000..967808d3 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailMessageReceiver.java @@ -0,0 +1,188 @@ +/* + * 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 java.util.Properties; +import javax.mail.Folder; +import javax.mail.FolderClosedException; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Store; +import javax.mail.URLName; +import javax.mail.event.MessageCountEvent; +import javax.mail.event.MessageCountListener; + +import com.sun.mail.imap.IMAPFolder; +import org.springframework.util.Assert; +import org.springframework.ws.transport.mail.support.MailUtils; +import org.springframework.ws.transport.support.AbstractMultiThreadedMessageReceiver; + +/** @author Arjen Poutsma */ +public class MailMessageReceiver extends AbstractMultiThreadedMessageReceiver { + + private Session session = Session.getInstance(new Properties(), null); + + private URLName storeUri; + + private Folder folder; + + private MessageCountHandler eventHandler; + + private Store store; + + private boolean supportsIdle; + + /** + * Set JavaMail properties for the {@link Session}. + *

+ * A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but + * not both. + *

+ * Non-default properties in this instance will override given JavaMail properties. + */ + public void setJavaMailProperties(Properties javaMailProperties) { + session = Session.getInstance(javaMailProperties, null); + } + + /** + * Set the JavaMail Session, possibly pulled from JNDI. + *

+ * Default is a new Session without defaults, that is completely configured via this instance's + * properties. + *

+ * If using a pre-configured Session, non-default properties in this instance will override the + * settings in the Session. + * + * @see #setJavaMailProperties + */ + public void setSession(Session session) { + Assert.notNull(session, "Session must not be null"); + this.session = session; + } + + public void setStoreUri(String storeUri) { + this.storeUri = new URLName(storeUri); + } + + public void afterPropertiesSet() throws Exception { + super.afterPropertiesSet(); + Assert.notNull(storeUri, "Property 'storeUri' is required"); + } + + protected void onActivate() throws Exception { + openFolder(); + } + + protected void onStart() { + if (logger.isInfoEnabled()) { + logger.info("Starting mail receiver [" + storeUri.toString() + "]"); + } + eventHandler = new MessageCountHandler(); + folder.addMessageCountListener(eventHandler); +/* + try { + if (folder instanceof IMAPFolder) { + IMAPFolder f = (IMAPFolder) folder; + logger.debug(folder.isOpen()); + logger.debug("Starting IDLE"); + f.idle(); + logger.debug("IDLE done"); + supportsIdle = true; + } + } + catch (MessagingException mex) { + supportsIdle = false; + } +*/ + logger.debug("Support IDLE: " + supportsIdle); + getTaskExecutor().execute(new MonitoringRunnable()); + } + + protected void onStop() { + if (logger.isInfoEnabled()) { + logger.info("Stopping mail receiver [" + storeUri.toString() + "]"); + } + if (eventHandler != null) { + folder.removeMessageCountListener(eventHandler); + eventHandler = null; + } + } + + protected void onShutdown() { + if (logger.isInfoEnabled()) { + logger.info("Shutting down mail receiver [" + storeUri.toString() + "]"); + } + closeFolder(); + } + + protected void openFolder() throws MessagingException, MailTransportException { + store = session.getStore(storeUri); + store.connect(); + folder = store.getFolder(storeUri); + if (folder == null || !folder.exists()) { + throw new MailTransportException("No default folder to receive from"); + } + folder.open(Folder.READ_WRITE); + logger.info("folder contains " + folder.getMessageCount() + " messages"); + } + + protected void closeFolder() { + MailUtils.closeFolder(folder); + MailUtils.closeService(store); + } + + private class MonitoringRunnable implements Runnable { + + public void run() { + try { + while (isRunning()) { + if (supportsIdle && folder instanceof IMAPFolder) { + IMAPFolder f = (IMAPFolder) folder; + logger.debug("IDLE starts"); + f.idle(); + logger.debug("IDLE done"); + } + else { + Thread.sleep(500); // sleep for freq milliseconds + + // This is to force the IMAP server to send us + // EXISTS notifications. + folder.getMessageCount(); + } + } + } + catch (InterruptedException ex) { + logger.warn(ex); + } + catch (MessagingException ex) { + logger.warn(ex); + } + } + } + + private class MessageCountHandler implements MessageCountListener { + + public void messagesAdded(MessageCountEvent event) { + Message[] msgs = event.getMessages(); + logger.info("Got " + msgs.length + " new messages"); + } + + public void messagesRemoved(MessageCountEvent e) { + } + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailMessageSender.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailMessageSender.java new file mode 100644 index 00000000..60370455 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailMessageSender.java @@ -0,0 +1,102 @@ +/* + * 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 java.io.IOException; +import java.util.Properties; +import javax.mail.Session; +import javax.mail.URLName; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.AddressException; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.ws.transport.WebServiceConnection; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.beans.factory.InitializingBean; + +/** @author Arjen Poutsma */ +public class MailMessageSender implements WebServiceMessageSender, InitializingBean { + + private Session session = Session.getInstance(new Properties(), null); + + private URLName storeUri; + + private URLName transportUri; + + private InternetAddress from; + + public void setFrom(String from) throws AddressException { + this.from = new InternetAddress(from); + } + + /** + * Set JavaMail properties for the {@link Session}. + *

+ * A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but + * not both. + *

+ * Non-default properties in this instance will override given JavaMail properties. + */ + public void setJavaMailProperties(Properties javaMailProperties) { + session = Session.getInstance(javaMailProperties, null); + } + + /** + * Set the JavaMail Session, possibly pulled from JNDI. + *

+ * Default is a new Session without defaults, that is completely configured via this instance's + * properties. + *

+ * If using a pre-configured Session, non-default properties in this instance will override the + * settings in the Session. + * + * @see #setJavaMailProperties + */ + public void setSession(Session session) { + Assert.notNull(session, "Session must not be null"); + this.session = session; + } + + public void setStoreUri(String storeUri) { + this.storeUri = new URLName(storeUri); + } + + public void setTransportUri(String transportUri) { + this.transportUri = new URLName(transportUri); + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(from, "Property 'from' is required"); + } + + public WebServiceConnection createConnection(String uri) throws IOException { + MailtoUri mailtoUri = new MailtoUri(uri); + MailSenderConnection connection = new MailSenderConnection(mailtoUri, session, from); + if (transportUri != null) { + connection.setTransportUri(transportUri); + } + if (storeUri != null) { + connection.setStoreUri(storeUri); + } + return connection; + } + + public boolean supports(String uri) { + return StringUtils.hasLength(uri) && uri.startsWith(MailtoUri.MAILTO_SCHEME); + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java new file mode 100644 index 00000000..80962942 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java @@ -0,0 +1,198 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import javax.mail.Header; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.URLName; +import javax.mail.internet.MimeMessage; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.activation.DataHandler; +import javax.activation.DataSource; + +import org.springframework.util.Assert; +import org.springframework.ws.transport.AbstractReceiverConnection; +import org.springframework.ws.transport.TransportConstants; +import org.springframework.ws.transport.mail.support.MailUtils; +import org.springframework.ws.transport.jms.JmsTransportException; +import org.springframework.ws.WebServiceMessage; + +/** @author Arjen Poutsma */ +public class MailReceiverConnection extends AbstractReceiverConnection { + + private final MimeMessage requestMessage; + + private final Session session; + + private MimeMessage responseMessage; + + private ByteArrayOutputStream responseBuffer; + + private String responseContentType; + + private URLName transportUri; + + public MailReceiverConnection(MimeMessage requestMessage, Session session) { + Assert.notNull(requestMessage, "'requestMessage' must not be null"); + Assert.notNull(session, "'session' must not be null"); + this.requestMessage = requestMessage; + this.session = session; + } + + public String getErrorMessage() throws IOException { + return null; + } + + public boolean hasError() throws IOException { + return false; + } + + public void setTransportUri(URLName transportUri) { + this.transportUri = transportUri; + } + + public void close() throws IOException { + } + + /* + * Receiving + */ + + protected Iterator getRequestHeaderNames() throws IOException { + try { + List headers = new ArrayList(); + Enumeration enumeration = requestMessage.getAllHeaders(); + while (enumeration.hasMoreElements()) { + Header header = (Header) enumeration.nextElement(); + headers.add(header.getName()); + } + return headers.iterator(); + } + catch (MessagingException ex) { + throw new IOException(ex.getMessage()); + } + } + + protected Iterator getRequestHeaders(String name) throws IOException { + try { + String[] headers = requestMessage.getHeader(name); + return Arrays.asList(headers).iterator(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected InputStream getRequestInputStream() throws IOException { + try { + return requestMessage.getDataHandler().getInputStream(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected void addResponseHeader(String name, String value) throws IOException { + try { + responseMessage.addHeader(name, value); + if (TransportConstants.HEADER_CONTENT_TYPE.equals(name)) { + responseContentType = value; + } + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected OutputStream getResponseOutputStream() throws IOException { + return responseBuffer; + } + + /* + * Sending + */ + + protected void onSendBeforeWrite(WebServiceMessage message) throws IOException { + try { + responseMessage = (MimeMessage) requestMessage.reply(false); + + responseBuffer = new ByteArrayOutputStream(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected void onSendAfterWrite(WebServiceMessage message) throws IOException { + Transport transport = null; + try { + requestMessage.setDataHandler( + new DataHandler(new ByteArrayDataSource(responseContentType, responseBuffer.toByteArray()))); + transport = session.getTransport(transportUri); + transport.connect(); + requestMessage.saveChanges(); + transport.sendMessage(requestMessage, requestMessage.getAllRecipients()); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + finally { + MailUtils.closeService(transport); + } + } + + private class ByteArrayDataSource implements DataSource { + + private byte[] data; + + private String contentType; + + public ByteArrayDataSource(String contentType, byte[] data) { + this.data = data; + this.contentType = contentType; + } + + public String getContentType() { + return contentType; + } + + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(data); + } + + public String getName() { + return "ByteArrayDataSource"; + } + + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailSenderConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailSenderConnection.java new file mode 100644 index 00000000..bda7c7b8 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailSenderConnection.java @@ -0,0 +1,282 @@ +/* + * 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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import javax.activation.DataHandler; +import javax.activation.DataSource; +import javax.mail.Flags; +import javax.mail.Folder; +import javax.mail.Header; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Store; +import javax.mail.Transport; +import javax.mail.URLName; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.search.HeaderTerm; +import javax.mail.search.SearchTerm; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.transport.AbstractSenderConnection; +import org.springframework.ws.transport.TransportConstants; +import org.springframework.ws.transport.mail.support.MailUtils; + +/** @author Arjen Poutsma */ +public class MailSenderConnection extends AbstractSenderConnection { + + private static final Log logger = LogFactory.getLog(MailSenderConnection.class); + + private final Session session; + + private final MailtoUri uri; + + private MimeMessage requestMessage; + + private MimeMessage responseMessage; + + private String requestContentType; + + private boolean deleteAfterReceive = false; + + private URLName storeUri; + + private URLName transportUri; + + private ByteArrayOutputStream requestBuffer; + + private InternetAddress from; + + protected MailSenderConnection(MailtoUri uri, Session session, InternetAddress from) { + Assert.notNull(uri, "'uri' must not be null"); + Assert.notNull(session, "'session' must not be null"); + this.uri = uri; + this.session = session; + this.from = from; + } + + public MimeMessage getRequestMessage() { + return requestMessage; + } + + public void setTransportUri(URLName transportUri) { + this.transportUri = transportUri; + } + + public void setStoreUri(URLName storeUri) { + this.storeUri = storeUri; + } + + /* + * Sending + */ + + protected void onSendBeforeWrite(WebServiceMessage message) throws IOException { + try { + requestMessage = new MimeMessage(session); + requestMessage.setFrom(from); + requestMessage.setRecipient(Message.RecipientType.TO, uri.getTo()); + if (uri.hasCc()) { + requestMessage.setRecipient(Message.RecipientType.CC, uri.getCc()); + } + if (uri.hasSubject()) { + requestMessage.setSubject(uri.getSubject()); + } + requestMessage.setSentDate(new Date()); + + requestBuffer = new ByteArrayOutputStream(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected void addRequestHeader(String name, String value) throws IOException { + try { + requestMessage.addHeader(name, value); + if (TransportConstants.HEADER_CONTENT_TYPE.equals(name)) { + requestContentType = value; + } + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected OutputStream getRequestOutputStream() throws IOException { + return requestBuffer; + } + + protected void onSendAfterWrite(WebServiceMessage message) throws IOException { + Transport transport = null; + try { + requestMessage.setDataHandler( + new DataHandler(new ByteArrayDataSource(requestContentType, requestBuffer.toByteArray()))); + transport = session.getTransport(transportUri); + transport.connect(); + requestMessage.saveChanges(); + transport.sendMessage(requestMessage, requestMessage.getAllRecipients()); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + finally { + MailUtils.closeService(transport); + } + } + + /* + * Receiving + */ + + protected void onReceiveBeforeRead() throws IOException { + Store store = null; + Folder folder = null; + try { + try { + Thread.sleep(5000); + } + catch (InterruptedException e) { + logger.debug(e); + } + store = session.getStore(storeUri); + store.connect(); + folder = store.getFolder(storeUri); + if (folder == null || !folder.exists()) { + throw new MailTransportException("No default folder to receive from"); + } + if (deleteAfterReceive) { + folder.open(Folder.READ_WRITE); + } + else { + folder.open(Folder.READ_ONLY); + } + String requestMessageId = requestMessage.getMessageID(); + SearchTerm searchTerm = new HeaderTerm(MailTransportConstants.HEADER_IN_REPLY_TO, requestMessageId); + Message[] responses = folder.search(searchTerm); + if (responses.length > 0) { + if (responses.length > 1) { + logger.warn("Received more than one response for request with ID [" + requestMessageId + "]"); + } + responseMessage = (MimeMessage) responses[0]; + } + if (deleteAfterReceive) { + responseMessage.setFlag(Flags.Flag.DELETED, true); + } + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + finally { + MailUtils.closeFolder(folder, deleteAfterReceive); + MailUtils.closeService(store); + } + } + + protected boolean hasResponse() throws IOException { + return responseMessage != null; + } + + protected Iterator getResponseHeaderNames() throws IOException { + try { + List headers = new ArrayList(); + Enumeration enumeration = responseMessage.getAllHeaders(); + while (enumeration.hasMoreElements()) { + Header header = (Header) enumeration.nextElement(); + headers.add(header.getName()); + } + return headers.iterator(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + protected Iterator getResponseHeaders(String name) throws IOException { + try { + String[] headers = responseMessage.getHeader(name); + return Arrays.asList(headers).iterator(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + + } + } + + protected InputStream getResponseInputStream() throws IOException { + try { + return responseMessage.getDataHandler().getInputStream(); + } + catch (MessagingException ex) { + throw new MailTransportException(ex); + } + } + + public boolean hasError() throws IOException { + return false; + } + + public String getErrorMessage() throws IOException { + return null; + } + + public void close() throws IOException { + } + + private class ByteArrayDataSource implements DataSource { + + private byte[] data; + + private String contentType; + + public ByteArrayDataSource(String contentType, byte[] data) { + this.data = data; + this.contentType = contentType; + } + + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(data); + } + + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + public String getContentType() { + return contentType; + } + + public String getName() { + return "ByteArrayDataSource"; + } + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java new file mode 100644 index 00000000..144f26eb --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java @@ -0,0 +1,28 @@ +/* + * 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 org.springframework.ws.transport.TransportConstants; + +/** @author Arjen Poutsma */ +public interface MailTransportConstants extends TransportConstants { + + /** The "In-Reply-To" header. */ + String HEADER_IN_REPLY_TO = "In-Reply-To"; + + +} 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 index 412455ec..c047fde1 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java @@ -24,15 +24,23 @@ import org.springframework.ws.transport.TransportException; /** @author Arjen Poutsma */ public class MailTransportException extends TransportException { + private MessagingException messagingException; + public MailTransportException(String msg) { super(msg); } - public MailTransportException(String msg, JMSException ex) { + public MailTransportException(String msg, MessagingException ex) { super(msg + ": " + ex.getMessage()); + initCause(ex); } public MailTransportException(MessagingException ex) { super(ex.getMessage()); + initCause(ex); + } + + public MessagingException getMessagingException() { + return messagingException; } } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailtoUri.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailtoUri.java new file mode 100644 index 00000000..baad6985 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailtoUri.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.transport.mail; + +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.Address; + +import org.springframework.util.Assert; +import org.springframework.ws.transport.support.ParameterizedUri; + +/** @author Arjen Poutsma */ +public class MailtoUri extends ParameterizedUri { + + static final String MAILTO_SCHEME = "mailto:"; + + public MailtoUri(String uri) { + super(uri); + Assert.isTrue(uri.startsWith(MAILTO_SCHEME), "Invalid uri: " + uri); + try { + InternetAddress.parse(getDestination(), false); + } + catch (AddressException ex) { + throw new IllegalArgumentException(ex); + } + } + + public InternetAddress getTo() throws AddressException { + return new InternetAddress(getDestination()); + } + + public String getSubject() { + return getParameter("subject"); + } + + public boolean hasSubject() { + return hasParameter("subject"); + } + + public boolean hasCc() { + return hasParameter("cc"); + } + + public InternetAddress getCc() throws AddressException { + return new InternetAddress(getParameter("cc")); + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MonitoringStrategy.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MonitoringStrategy.java new file mode 100644 index 00000000..2222abbf --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MonitoringStrategy.java @@ -0,0 +1,22 @@ +/* + * 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; + +/** @author Arjen Poutsma */ +public interface MonitoringStrategy { + +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/support/MailUtils.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/support/MailUtils.java new file mode 100644 index 00000000..0314883a --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/support/MailUtils.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.mail.support; + +import javax.mail.Folder; +import javax.mail.MessagingException; +import javax.mail.Service; +import javax.mail.Store; +import javax.mail.Transport; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** @author Arjen Poutsma */ +public abstract class MailUtils { + + private static final Log logger = LogFactory.getLog(MailUtils.class); + + /** + * Close the given JavaMail Service and ignore any thrown exception. This is useful for typical finally + * blocks in manual JavaMail code. + * + * @param service the JavaMail Service to close (may be null) + * @see Transport + * @see Store + */ + public static void closeService(Service service) { + if (service != null) { + try { + service.close(); + } + catch (MessagingException ex) { + logger.debug("Could not close JavaMail Transport", ex); + } + } + } + + /** + * Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical finally + * blocks in manual JavaMail code. + * + * @param folder the JavaMail Folder to close (may be null) + */ + + public static void closeFolder(Folder folder) { + closeFolder(folder, false); + } + + /** + * Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical finally + * blocks in manual JavaMail code. + * + * @param folder the JavaMail Folder to close (may be null) + * @param expunge whether all deleted messages should be expunged from the folder + */ + public static void closeFolder(Folder folder, boolean expunge) { + if (folder != null) { + try { + folder.close(expunge); + } + catch (MessagingException ex) { + logger.debug("Could not close JavaMail Transport", ex); + } + } + } + + +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMultiThreadedMessageReceiver.java b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMultiThreadedMessageReceiver.java new file mode 100644 index 00000000..517fe756 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMultiThreadedMessageReceiver.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.support; + +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; +import org.springframework.util.ClassUtils; +import org.springframework.scheduling.commonj.WorkManagerTaskExecutor; + +/** + * Abstract base class for standalone, server-side transport objects. Contains a Spring {@link TaskExecutor}, and + * various lifecycle callbacks. + * + * @author Arjen Poutsma + */ +public abstract class AbstractMultiThreadedMessageReceiver extends AbstractStandaloneMessagingReceiver + implements BeanNameAware { + + /** Default thread name prefix. */ + public final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils.getShortName(getClass()) + "-"; + + private TaskExecutor taskExecutor; + + private String beanName; + + /** Returns the task executor. */ + public TaskExecutor getTaskExecutor() { + return taskExecutor; + } + + /** + * 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 + * WorkManagerTaskExecutor} to integrate with WebSphere or WebLogic. + */ + public void setTaskExecutor(TaskExecutor taskExecutor) { + this.taskExecutor = taskExecutor; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public void afterPropertiesSet() throws Exception { + if (taskExecutor == null) { + taskExecutor = createDefaultTaskExecutor(); + } + super.afterPropertiesSet(); + } + + /** + * 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); + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessagingReceiver.java similarity index 58% rename from sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java rename to sandbox/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessagingReceiver.java index 557131f6..fb3676f9 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessagingReceiver.java @@ -16,24 +16,12 @@ 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()) + "-"; +/** @author Arjen Poutsma */ +public abstract class AbstractStandaloneMessagingReceiver extends SimpleWebServiceMessageReceiverObjectSupport + implements Lifecycle, DisposableBean { private volatile boolean active = false; @@ -43,39 +31,6 @@ public abstract class AbstractMessagingContainer extends SimpleWebServiceMessage 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) { @@ -91,23 +46,15 @@ public abstract class AbstractMessagingContainer extends SimpleWebServiceMessage } /** - * Create a default TaskExecutor. Called if no explicit TaskExecutor has been specified. + * Set whether to automatically start the listener after initialization. *

- * 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) + * Default is true; set this to false to allow for manual startup. */ - protected TaskExecutor createDefaultTaskExecutor() { - String threadNamePrefix = beanName != null ? beanName + "-" : DEFAULT_THREAD_NAME_PREFIX; - return new SimpleAsyncTaskExecutor(threadNamePrefix); + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; } public void afterPropertiesSet() throws Exception { - super.afterPropertiesSet(); - if (taskExecutor == null) { - taskExecutor = createDefaultTaskExecutor(); - } activate(); } @@ -167,5 +114,4 @@ public abstract class AbstractMessagingContainer extends SimpleWebServiceMessage protected abstract void onStop(); protected abstract void onShutdown(); - } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/ParameterizedUri.java b/sandbox/src/main/java/org/springframework/ws/transport/support/ParameterizedUri.java new file mode 100644 index 00000000..58be7494 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ws/transport/support/ParameterizedUri.java @@ -0,0 +1,84 @@ +/* + * 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 java.util.Map; +import java.util.StringTokenizer; + +import org.springframework.core.CollectionFactory; +import org.springframework.util.Assert; + +/** @author Arjen Poutsma */ +public class ParameterizedUri { + + private final String uri; + + private final String scheme; + + // keys are string parameter names; values are string parameter values + private final Map parameters = CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(5); + + private final String destination; + + public ParameterizedUri(String uri) { + Assert.hasLength(uri, "'uri' must not be empty"); + this.uri = uri; + int scIdx = uri.indexOf(':'); + Assert.isTrue(scIdx != -1, uri + " does contain scheme"); + scheme = uri.substring(0, scIdx); + Assert.isTrue(uri.length() > scheme.length(), uri + " does not have a destination"); + int paramStart = uri.indexOf('?'); + if (paramStart == -1) { + destination = uri.substring(scIdx + 1); + } + else { + destination = uri.substring(scIdx + 1, paramStart); + parseParameters(uri.substring(paramStart + 1)); + } + } + + private void parseParameters(String parametersString) { + StringTokenizer params = new StringTokenizer(parametersString, "&"); + while (params.hasMoreTokens()) { + String param = params.nextToken(); + int paramSep = param.indexOf('='); + if (paramSep == -1) { + throw new IllegalArgumentException(param + " is not a valid parameter: it has no '='"); + } + String paramName = param.substring(0, paramSep); + String paramValue = param.substring(paramSep + 1); + parameters.put(paramName, paramValue); + } + } + + /** Returns the destination of the uri. */ + protected String getDestination() { + return destination; + } + + public String toString() { + return uri; + } + + protected String getParameter(String paramName) { + return (String) parameters.get(paramName); + } + + protected boolean hasParameter(String paramName) { + return parameters.containsKey(paramName); + } +} diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageReceiver.java similarity index 70% rename from sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java rename to sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageReceiver.java index 9f05db5d..886ecb7d 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageReceiver.java @@ -24,10 +24,11 @@ import java.net.Socket; import java.net.UnknownHostException; import org.springframework.ws.transport.WebServiceConnection; -import org.springframework.ws.transport.support.AbstractMessagingContainer; +import org.springframework.ws.transport.support.AbstractMultiThreadedMessageReceiver; /** @author Arjen Poutsma */ -public class TcpMessagingContainer extends AbstractMessagingContainer { +public class TcpMessageReceiver extends AbstractMultiThreadedMessageReceiver { + public static final int DEFAULT_PORT = 8081; private ServerSocket serverSocket; @@ -35,7 +36,7 @@ public class TcpMessagingContainer extends AbstractMessagingContainer { private int backlog = -1; - private int port = -1; + private int port = DEFAULT_PORT; /** Sets the port the server will bind to. */ public void setPort(int port) { @@ -58,58 +59,34 @@ public class TcpMessagingContainer extends AbstractMessagingContainer { 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() + "]"); + logger.info("Starting tcp receiver [" + serverSocket.getLocalSocketAddress() + "]"); } getTaskExecutor().execute(new SocketAcceptingRunnable()); } protected void onStop() { if (logger.isInfoEnabled()) { - logger.info("Stopping tcp server [" + serverSocket.getLocalSocketAddress() + "]"); + logger.info("Stopping tcp receiver [" + serverSocket.getLocalSocketAddress() + "]"); } } protected void onShutdown() { if (logger.isInfoEnabled()) { - logger.info("Shutting down tcp server [" + serverSocket.getLocalSocketAddress() + "]"); + logger.info("Shutting down tcp receiver [" + 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() + * Establish a ServerSocket for this receiver. */ 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); } @@ -154,7 +131,7 @@ public class TcpMessagingContainer extends AbstractMessagingContainer { } public void run() { - WebServiceConnection connection = new TcpReceivingWebServiceConnection(socket); + WebServiceConnection connection = new TcpReceiverConnection(socket); try { handleConnection(connection); } 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 index 637dc5a2..9be37a0b 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java @@ -17,57 +17,49 @@ 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.util.StringUtils; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageSender; /** @author Arjen Poutsma */ -public class TcpMessageSender implements WebServiceMessageSender, InitializingBean { +public class TcpMessageSender implements WebServiceMessageSender { - private InetAddress address; + private static final String TCP_SCHEME = "tcp://"; - private int port = -1; + public static final int DEFAULT_PORT = 8081; 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 boolean supports(String uri) { + return StringUtils.hasLength(uri) && uri.startsWith(TCP_SCHEME); } - 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"); + public WebServiceConnection createConnection(String uri) throws IOException { + Assert.isTrue(uri.startsWith(TCP_SCHEME), "Invalid uri: " + uri); + uri = uri.substring(TCP_SCHEME.length()); + int idx = uri.indexOf(':'); + String hostname; + int port; + if (idx != -1) { + hostname = uri.substring(0, idx); + port = Integer.parseInt(uri.substring(idx + 1)); + } else { + hostname = uri; + port = DEFAULT_PORT; } - Assert.notNull(address, "address is required"); - + Socket socket = new Socket(); + SocketAddress socketAddress = new InetSocketAddress(hostname, port); + socket.connect(socketAddress, timeOut); + return new TcpSenderConnection(socket); } } diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceiverConnection.java similarity index 83% rename from sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java rename to sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceiverConnection.java index b57babe7..e864a663 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceiverConnection.java @@ -26,18 +26,26 @@ import java.util.Collections; import java.util.Iterator; import org.springframework.util.Assert; -import org.springframework.ws.transport.AbstractReceivingWebServiceConnection; +import org.springframework.ws.transport.AbstractReceiverConnection; /** @author Arjen Poutsma */ -public class TcpReceivingWebServiceConnection extends AbstractReceivingWebServiceConnection { +public class TcpReceiverConnection extends AbstractReceiverConnection { private final Socket socket; - public TcpReceivingWebServiceConnection(Socket socket) { + protected TcpReceiverConnection(Socket socket) { Assert.notNull(socket, "socket must not be null"); this.socket = socket; } + public boolean hasError() throws IOException { + return false; + } + + public String getErrorMessage() throws IOException { + return null; + } + public void close() throws IOException { socket.close(); } @@ -73,7 +81,7 @@ public class TcpReceivingWebServiceConnection extends AbstractReceivingWebServic }; } - protected void sendResponse() throws IOException { + protected void sendResponse(boolean sentFault) 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/TcpSenderConnection.java similarity index 86% rename from sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java rename to sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSenderConnection.java index 005ec35c..ad5c0a4f 100644 --- a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java +++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSenderConnection.java @@ -26,14 +26,14 @@ import java.util.Collections; import java.util.Iterator; import org.springframework.util.Assert; -import org.springframework.ws.transport.AbstractSendingWebServiceConnection; +import org.springframework.ws.transport.AbstractSenderConnection; /** @author Arjen Poutsma */ -public class TcpSendingWebServiceConnection extends AbstractSendingWebServiceConnection { +public class TcpSenderConnection extends AbstractSenderConnection { private final Socket socket; - public TcpSendingWebServiceConnection(Socket socket) { + protected TcpSenderConnection(Socket socket) { Assert.notNull(socket, "socket must not be null"); this.socket = socket; } @@ -42,6 +42,14 @@ public class TcpSendingWebServiceConnection extends AbstractSendingWebServiceCon socket.close(); } + public boolean hasError() throws IOException { + return false; + } + + public String getErrorMessage() throws IOException { + return null; + } + protected void addRequestHeader(String name, String value) throws IOException { } diff --git a/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java index b40d0948..aff2af56 100644 --- a/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java +++ b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java @@ -18,14 +18,14 @@ package org.springframework.ws.transport; import javax.xml.transform.Transformer; -import junit.framework.Assert; import org.springframework.ws.context.MessageContext; import org.springframework.xml.transform.TransformerObjectSupport; +import org.springframework.util.Assert; public class SimpleTestingMessageReceiver extends TransformerObjectSupport implements WebServiceMessageReceiver { public void receive(MessageContext messageContext) throws Exception { - Assert.assertNotNull("MessageContext is null", messageContext); + Assert.notNull(messageContext, "MessageContext is null"); logger.info("Received message"); Transformer transformer = createTransformer(); transformer.transform(messageContext.getRequest().getPayloadSource(), diff --git a/sandbox/src/test/java/org/springframework/ws/transport/jms/JmsUriTest.java b/sandbox/src/test/java/org/springframework/ws/transport/jms/JmsUriTest.java index 028d72d5..87f6465a 100644 --- a/sandbox/src/test/java/org/springframework/ws/transport/jms/JmsUriTest.java +++ b/sandbox/src/test/java/org/springframework/ws/transport/jms/JmsUriTest.java @@ -35,7 +35,6 @@ public class JmsUriTest extends TestCase { assertEquals("Invalid prority", 8, uri.getPriority()); assertEquals("Invalid time to live", 10, uri.getTimeToLive()); assertEquals("Invalid reply to name", "interested", uri.getReplyTo()); - assertEquals("Invalid custom property", "mystuff", uri.getCustomParameter("userprop")); } diff --git a/sandbox/src/test/java/org/springframework/ws/transport/jms/JmsTransportUtilsTest.java b/sandbox/src/test/java/org/springframework/ws/transport/jms/support/JmsTransportUtilsTest.java similarity index 88% rename from sandbox/src/test/java/org/springframework/ws/transport/jms/JmsTransportUtilsTest.java rename to sandbox/src/test/java/org/springframework/ws/transport/jms/support/JmsTransportUtilsTest.java index 90aa01cb..350665eb 100644 --- a/sandbox/src/test/java/org/springframework/ws/transport/jms/JmsTransportUtilsTest.java +++ b/sandbox/src/test/java/org/springframework/ws/transport/jms/support/JmsTransportUtilsTest.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package org.springframework.ws.transport.jms; +package org.springframework.ws.transport.jms.support; import junit.framework.TestCase; +import org.springframework.ws.transport.jms.support.JmsTransportUtils; public class JmsTransportUtilsTest extends TestCase { diff --git a/sandbox/src/test/java/org/springframework/ws/transport/mail/Driver.java b/sandbox/src/test/java/org/springframework/ws/transport/mail/Driver.java new file mode 100644 index 00000000..107a6714 --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/transport/mail/Driver.java @@ -0,0 +1,32 @@ +/* + * 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 java.io.IOException; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** @author Arjen Poutsma */ +public class Driver { + + public static void main(String[] args) throws IOException { + new ClassPathXmlApplicationContext("applicationContext.xml", Driver.class); + System.out.println("Started...."); + System.in.read(); + } + +} diff --git a/sandbox/src/test/java/org/springframework/ws/transport/mail/MailMessageSenderIntegrationTest.java b/sandbox/src/test/java/org/springframework/ws/transport/mail/MailMessageSenderIntegrationTest.java new file mode 100644 index 00000000..1d270489 --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/transport/mail/MailMessageSenderIntegrationTest.java @@ -0,0 +1,70 @@ +/* + * 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 java.util.Properties; +import javax.jms.BytesMessage; +import javax.xml.soap.MessageFactory; +import javax.xml.soap.SOAPConstants; +import javax.xml.soap.SOAPMessage; +import javax.mail.URLName; + +import junit.framework.TestCase; +import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.saaj.SaajSoapMessage; +import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; +import org.springframework.ws.transport.WebServiceConnection; + +public class MailMessageSenderIntegrationTest extends TestCase { + + private MailMessageSender messageSender; + + private MessageFactory messageFactory; + + private static final String URI = "mailto:ajwpi21@xs4all.nl?subject=SOAP Test"; + + private static final String SOAP_ACTION = "http://springframework.org/DoIt"; + + protected void setUp() throws Exception { + Properties properties = new Properties(); + properties.setProperty("mail.smtp.host", "smtp.xs4all.nl"); + messageSender = new MailMessageSender(); + messageSender.setStoreUri("pop3://ajwpi21:sjantaL.@pop.xs4all.nl/INBOX"); + messageSender.setTransportUri("smtp://ajwpi21:sjantaL.@smtp.xs4all.nl"); + messageSender.setFrom("Arjen Poutsma "); + messageSender.setJavaMailProperties(properties); + messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); + } + + public void testSendAndReceiveQueueNoResponse() throws Exception { + WebServiceConnection connection = null; + try { + connection = messageSender.createConnection(URI); + SOAPMessage saajMessage = messageFactory.createMessage(); + SoapMessage soapRequest = new SaajSoapMessage(saajMessage); + soapRequest.setSoapAction(SOAP_ACTION); + connection.send(soapRequest); +// SoapMessage response = (SoapMessage) connection.receive(new SaajSoapMessageFactory(messageFactory)); + } + finally { + if (connection != null) { + connection.close(); + } + } + } + +} \ No newline at end of file diff --git a/sandbox/src/test/java/org/springframework/ws/transport/tcp/Driver.java b/sandbox/src/test/java/org/springframework/ws/transport/tcp/Driver.java new file mode 100644 index 00000000..7d55643a --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ws/transport/tcp/Driver.java @@ -0,0 +1,33 @@ +/* + * 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.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** @author Arjen Poutsma */ +public class Driver { + + public static void main(String[] args) throws IOException { + new ClassPathXmlApplicationContext("applicationContext.xml", Driver.class); + System.out.println("Started...."); + System.in.read(); + } + +} diff --git a/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java b/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpMessageReceiverIntegrationTest.java similarity index 89% rename from sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java rename to sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpMessageReceiverIntegrationTest.java index 9e957a04..6de7345f 100644 --- a/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java +++ b/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpMessageReceiverIntegrationTest.java @@ -31,7 +31,7 @@ import org.springframework.ws.client.core.WebServiceTemplate; import org.springframework.ws.transport.WebServiceMessageSender; import org.springframework.xml.transform.StringSource; -public class TcpServerIntegrationTest extends AbstractDependencyInjectionSpringContextTests { +public class TcpMessageReceiverIntegrationTest extends AbstractDependencyInjectionSpringContextTests { private WebServiceMessageFactory messageFactory; @@ -54,7 +54,7 @@ public class TcpServerIntegrationTest extends AbstractDependencyInjectionSpringC " \n" + ""; public void testServer() throws IOException, InterruptedException { - Socket socket = new Socket("localhost", 9999); + Socket socket = new Socket("localhost", TcpMessageReceiver.DEFAULT_PORT); Writer writer = null; BufferedReader reader = null; try { @@ -74,8 +74,9 @@ public class TcpServerIntegrationTest extends AbstractDependencyInjectionSpringC } public void testTemplate() throws Exception { - WebServiceTemplate template = new WebServiceTemplate(messageFactory, messageSender); - template.sendAndReceive(new StringSource(REQUEST), new StreamResult(System.out)); + WebServiceTemplate template = new WebServiceTemplate(messageFactory); + template.setMessageSender(messageSender); + template.sendAndReceive("tcp://localhost",new StringSource(REQUEST), new StreamResult(System.out)); } protected String[] getConfigLocations() { diff --git a/sandbox/src/test/resources/org/springframework/ws/transport/mail/applicationContext.xml b/sandbox/src/test/resources/org/springframework/ws/transport/mail/applicationContext.xml new file mode 100644 index 00000000..544cc4e1 --- /dev/null +++ b/sandbox/src/test/resources/org/springframework/ws/transport/mail/applicationContext.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.context.Lifecycle + + + + + + + + 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 index 449cb071..29c90894 100644 --- a/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml +++ b/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml @@ -2,19 +2,14 @@ - - - + - - - - + @@ -24,7 +19,7 @@ - +