/* * Copyright 2005-2014 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.jms; import java.io.IOException; import java.net.URI; import javax.jms.BytesMessage; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import org.springframework.jms.connection.ConnectionFactoryUtils; import org.springframework.jms.core.MessagePostProcessor; import org.springframework.jms.support.JmsUtils; import org.springframework.jms.support.destination.JmsDestinationAccessor; import org.springframework.util.StringUtils; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageSender; import org.springframework.ws.transport.jms.support.JmsTransportUtils; /** * {@link WebServiceMessageSender} implementation that uses JMS {@link Message}s. Requires a JMS {@link * ConnectionFactory} to operate. * * <p>This message sender supports URI's of the following format: <blockquote> <tt><b>jms:</b></tt><i>destination</i>[<tt><b>?</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>][<tt><b>&</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>]* * </blockquote> where the characters <tt><b>:</b></tt>, <tt><b>?</b></tt>, and <tt><b>&</b></tt> stand for * themselves. The <i>destination</i> represents the name of the {@link Queue} or {@link Topic} that will be resolved by * the {@link #getDestinationResolver() destination resolver}. Valid <i>param-name</i> include: * * <blockquote> * <table> * <tr><th><i>param-name</i></th><th><i>Description</i></th></tr> * <tr> * <td><tt>deliveryMode</tt></td> * <td>Indicates whether the request message is persistent or not. This may be <tt>PERSISTENT</tt> or * <tt>NON_PERSISTENT</tt>. See {@link MessageProducer#setDeliveryMode(int)}</td> * </tr> * <tr> * <td><tt>messageType</tt></td> * <td>The message type. This may be <tt>BINARY_MESSAGE</tt> (the default) or <tt>TEXT_MESSAGE</tt></td> * </tr> * <tr> * <td><tt>priority</tt></td> * <td>The JMS priority (0-9) associated with the request message. See * {@link MessageProducer#setPriority(int)}</td> * </tr> * <tr> * <td><tt>replyToName</tt></td> * <td>The name of the destination to which the response message must be sent, that will be resolved by * the {@link #getDestinationResolver() destination resolver}.</td> * </tr> * <tr> * <td><tt>timeToLive</tt></td> * <td>The lifetime, in milliseconds, of the request message. See * {@link MessageProducer#setTimeToLive(long)}</td> * </tr> * </table> * </blockquote> * * <p>If the <tt>replyToName</tt> is not set, a {@link Session#createTemporaryQueue() temporary queue} is used. * * <p>This class uses {@link BytesMessage} messages by default, but can be configured to send {@link TextMessage} messages * instead. <b>Note</b> that {@code BytesMessages} are preferred, since {@code TextMessages} do not support * attachments and character encodings reliably. * * <p>Some examples of JMS URIs are: * * <blockquote> <tt>jms:SomeQueue</tt><br> <tt>jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT</tt><br> * <tt>jms:RequestQueue?replyToName=ResponseQueueName</tt><br> <tt>jms:Queue?messageType=TEXT_MESSAGE</blockquote> * * @author Arjen Poutsma * @see <a href="http://tools.ietf.org/id/draft-merrick-jms-iri-00.txt">IRI Scheme for Java(tm) Message Service 1.0</a> * @since 1.5.0 */ public class JmsMessageSender extends JmsDestinationAccessor implements WebServiceMessageSender { /** Default timeout for receive operations: -1 indicates a blocking receive without timeout. */ public static final long DEFAULT_RECEIVE_TIMEOUT = -1; /** Default encoding used to read fromn and write to {@link TextMessage} messages. */ public static final String DEFAULT_TEXT_MESSAGE_ENCODING = "UTF-8"; private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT; private String textMessageEncoding = DEFAULT_TEXT_MESSAGE_ENCODING; private MessagePostProcessor postProcessor; /** * Create a new {@code JmsMessageSender} * * <p><b>Note</b>: The ConnectionFactory has to be set before using the instance. This constructor can be used to * prepare a JmsTemplate via a BeanFactory, typically setting the ConnectionFactory via {@link * #setConnectionFactory(ConnectionFactory)}. * * @see #setConnectionFactory(ConnectionFactory) */ public JmsMessageSender() { } /** * Create a new {@code JmsMessageSender}, given a ConnectionFactory. * * @param connectionFactory the ConnectionFactory to obtain Connections from */ public JmsMessageSender(ConnectionFactory connectionFactory) { setConnectionFactory(connectionFactory); } /** * Set the timeout to use for receive calls. The default is -1, which means no timeout. * * @see MessageConsumer#receive(long) */ public void setReceiveTimeout(long receiveTimeout) { this.receiveTimeout = receiveTimeout; } /** Sets the encoding used to read from {@link TextMessage} messages. Defaults to {@code UTF-8}. */ public void setTextMessageEncoding(String textMessageEncoding) { this.textMessageEncoding = textMessageEncoding; } /** * Sets the optional {@link MessagePostProcessor} to further modify outgoing messages after the XML contents has * been set. */ public void setPostProcessor(MessagePostProcessor postProcessor) { this.postProcessor = postProcessor; } @Override public WebServiceConnection createConnection(URI uri) throws IOException { Connection jmsConnection = null; Session jmsSession = null; try { jmsConnection = createConnection(); jmsSession = createSession(jmsConnection); Destination requestDestination = resolveRequestDestination(jmsSession, uri); Message requestMessage = createRequestMessage(jmsSession, uri); JmsSenderConnection wsConnection = new JmsSenderConnection(getConnectionFactory(), jmsConnection, jmsSession, requestDestination, requestMessage); wsConnection.setDeliveryMode(JmsTransportUtils.getDeliveryMode(uri)); wsConnection.setPriority(JmsTransportUtils.getPriority(uri)); wsConnection.setReceiveTimeout(receiveTimeout); wsConnection.setResponseDestination(resolveResponseDestination(jmsSession, uri)); wsConnection.setTimeToLive(JmsTransportUtils.getTimeToLive(uri)); wsConnection.setTextMessageEncoding(textMessageEncoding); wsConnection.setSessionTransacted(isSessionTransacted()); wsConnection.setPostProcessor(postProcessor); return wsConnection; } catch (JMSException ex) { JmsUtils.closeSession(jmsSession); ConnectionFactoryUtils.releaseConnection(jmsConnection, getConnectionFactory(), true); throw new JmsTransportException(ex); } } @Override public boolean supports(URI uri) { return uri.getScheme().equals(JmsTransportConstants.JMS_URI_SCHEME); } private Destination resolveRequestDestination(Session session, URI uri) throws JMSException { return resolveDestinationName(session, JmsTransportUtils.getDestinationName(uri)); } private Destination resolveResponseDestination(Session session, URI uri) throws JMSException { String destinationName = JmsTransportUtils.getReplyToName(uri); return StringUtils.hasLength(destinationName) ? resolveDestinationName(session, destinationName) : null; } private Message createRequestMessage(Session session, URI uri) throws JMSException { int messageType = JmsTransportUtils.getMessageType(uri); if (messageType == JmsTransportConstants.BYTES_MESSAGE_TYPE) { return session.createBytesMessage(); } else if (messageType == JmsTransportConstants.TEXT_MESSAGE_TYPE) { return session.createTextMessage(); } else { throw new IllegalArgumentException("Invalid message type [" + messageType + "]."); } } }