View Javadoc

1   /*
2    * Copyright 2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.transport.jms;
18  
19  import java.io.IOException;
20  import java.net.URI;
21  import javax.jms.BytesMessage;
22  import javax.jms.Connection;
23  import javax.jms.ConnectionFactory;
24  import javax.jms.Destination;
25  import javax.jms.JMSException;
26  import javax.jms.Message;
27  import javax.jms.MessageConsumer;
28  import javax.jms.MessageProducer;
29  import javax.jms.Queue;
30  import javax.jms.Session;
31  import javax.jms.TextMessage;
32  import javax.jms.Topic;
33  
34  import org.springframework.jms.connection.ConnectionFactoryUtils;
35  import org.springframework.jms.core.MessagePostProcessor;
36  import org.springframework.jms.support.JmsUtils;
37  import org.springframework.jms.support.destination.JmsDestinationAccessor;
38  import org.springframework.util.StringUtils;
39  import org.springframework.ws.transport.WebServiceConnection;
40  import org.springframework.ws.transport.WebServiceMessageSender;
41  import org.springframework.ws.transport.jms.support.JmsTransportUtils;
42  
43  /**
44   * {@link WebServiceMessageSender} implementation that uses JMS {@link Message}s. Requires a JMS {@link
45   * ConnectionFactory} to operate.
46   * <p/>
47   * 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>&amp;</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>]*
48   * </blockquote> where the characters <tt><b>:</b></tt>, <tt><b>?</b></tt>, and <tt><b>&amp;</b></tt> stand for
49   * themselves. The <i>destination</i> represents the name of the {@link Queue} or {@link Topic} that will be resolved by
50   * the {@link #getDestinationResolver() destination resolver}. Valid <i>param-name</i> include:
51   * <p/>
52   * <blockquote><table> <tr><th><i>param-name</i></th><th><i>Description</i></th></tr>
53   * <tr><td><tt>deliveryMode</tt></td><td>Indicates whether the request message is persistent or not. This may be
54   * <tt>PERSISTENT</tt> or <tt>NON_PERSISTENT</tt>. See {@link MessageProducer#setDeliveryMode(int)}</td></tr>
55   * <tr><td><tt>messageType</tt></td><td>The message type. This may be <tt>BINARY_MESSAGE</tt> (the default) or
56   * <tt>TEXT_MESSAGE</tt></td></tr> <tr><td><tt>priority</tt></td><td>The JMS priority (0-9) associated with the request
57   * message. See {@link MessageProducer#setPriority(int)}</td></tr> <tr><td><tt>replyToName</tt></td><td>The name of the
58   * destination to which the response message must be sent, that will be resolved by the {@link #getDestinationResolver()
59   * destination resolver}.</td></tr> <tr><td><tt>timeToLive</tt></td><td>The lifetime, in milliseconds, of the request
60   * message. See {@link MessageProducer#setTimeToLive(long)}</td></tr> </table></blockquote>
61   * <p/>
62   * If the <tt>replyToName</tt> is not set, a {@link Session#createTemporaryQueue() temporary queue} is used.
63   * <p/>
64   * This class uses {@link BytesMessage} messages by default, but can be configured to send {@link TextMessage} messages
65   * instead. <b>Note</b> that <code>BytesMessages</code> are prefered, since <code>TextMessages</code> do not support
66   * attachments and charactering encodings reliably.
67   * <p/>
68   * Some examples of JMS URIs are:
69   * <p/>
70   * <blockquote> <tt>jms:SomeQueue</tt><br> <tt>jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT</tt><br>
71   * <tt>jms:RequestQueue?replyToName=ResponseQueueName</tt><br> <tt>jms:Queue?messageType=TEXT_MESSAGE</blockquote>
72   *
73   * @author Arjen Poutsma
74   * @see <a href="http://tools.ietf.org/id/draft-merrick-jms-iri-00.txt">IRI Scheme for Java(tm) Message Service 1.0</a>
75   * @since 1.5.0
76   */
77  public class JmsMessageSender extends JmsDestinationAccessor implements WebServiceMessageSender {
78  
79      /** Default timeout for receive operations: -1 indicates a blocking receive without timeout. */
80      public static final long DEFAULT_RECEIVE_TIMEOUT = -1;
81  
82      /** Default encoding used to read fromn and write to {@link TextMessage} messages. */
83      public static final String DEFAULT_TEXT_MESSAGE_ENCODING = "UTF-8";
84  
85      private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
86  
87      private String textMessageEncoding = DEFAULT_TEXT_MESSAGE_ENCODING;
88  
89      private MessagePostProcessor postProcessor;
90  
91      /**
92       * Create a new <code>JmsMessageSender</code>
93       * <p/>
94       * <b>Note</b>: The ConnectionFactory has to be set before using the instance. This constructor can be used to
95       * prepare a JmsTemplate via a BeanFactory, typically setting the ConnectionFactory via {@link
96       * #setConnectionFactory(ConnectionFactory)}.
97       *
98       * @see #setConnectionFactory(ConnectionFactory)
99       */
100     public JmsMessageSender() {
101     }
102 
103     /**
104      * Create a new <code>JmsMessageSender</code>, given a ConnectionFactory.
105      *
106      * @param connectionFactory the ConnectionFactory to obtain Connections from
107      */
108     public JmsMessageSender(ConnectionFactory connectionFactory) {
109         setConnectionFactory(connectionFactory);
110     }
111 
112     /**
113      * Set the timeout to use for receive calls. The default is -1, which means no timeout.
114      *
115      * @see MessageConsumer#receive(long)
116      */
117     public void setReceiveTimeout(long receiveTimeout) {
118         this.receiveTimeout = receiveTimeout;
119     }
120 
121     /** Sets the encoding used to read from {@link TextMessage} messages. Defaults to <code>UTF-8</code>. */
122     public void setTextMessageEncoding(String textMessageEncoding) {
123         this.textMessageEncoding = textMessageEncoding;
124     }
125 
126     /**
127      * Sets the optional {@link MessagePostProcessor} to further modify outgoing messages after the XML contents has
128      * been set.
129      */
130     public void setPostProcessor(MessagePostProcessor postProcessor) {
131         this.postProcessor = postProcessor;
132     }
133 
134     public WebServiceConnection createConnection(URI uri) throws IOException {
135         Connection jmsConnection = null;
136         Session jmsSession = null;
137         try {
138             jmsConnection = createConnection();
139             jmsSession = createSession(jmsConnection);
140             Destination requestDestination = resolveRequestDestination(jmsSession, uri);
141             Message requestMessage = createRequestMessage(jmsSession, uri);
142             JmsSenderConnection wsConnection =
143                     new JmsSenderConnection(getConnectionFactory(), jmsConnection, jmsSession, requestDestination,
144                             requestMessage);
145             wsConnection.setDeliveryMode(JmsTransportUtils.getDeliveryMode(uri));
146             wsConnection.setPriority(JmsTransportUtils.getPriority(uri));
147             wsConnection.setReceiveTimeout(receiveTimeout);
148             wsConnection.setResponseDestination(resolveResponseDestination(jmsSession, uri));
149             wsConnection.setTimeToLive(JmsTransportUtils.getTimeToLive(uri));
150             wsConnection.setTextMessageEncoding(textMessageEncoding);
151             wsConnection.setSessionTransacted(isSessionTransacted());
152             wsConnection.setPostProcessor(postProcessor);
153             return wsConnection;
154         }
155         catch (JMSException ex) {
156             JmsUtils.closeSession(jmsSession);
157             ConnectionFactoryUtils.releaseConnection(jmsConnection, getConnectionFactory(), true);
158             throw new JmsTransportException(ex);
159         }
160     }
161 
162     public boolean supports(URI uri) {
163         return uri.getScheme().equals(JmsTransportConstants.JMS_URI_SCHEME);
164     }
165 
166     private Destination resolveRequestDestination(Session session, URI uri) throws JMSException {
167         return resolveDestinationName(session, JmsTransportUtils.getDestinationName(uri));
168     }
169 
170     private Destination resolveResponseDestination(Session session, URI uri) throws JMSException {
171         String destinationName = JmsTransportUtils.getReplyToName(uri);
172         return StringUtils.hasLength(destinationName) ? resolveDestinationName(session, destinationName) : null;
173     }
174 
175     private Message createRequestMessage(Session session, URI uri) throws JMSException {
176         int messageType = JmsTransportUtils.getMessageType(uri);
177         if (messageType == JmsTransportConstants.BYTES_MESSAGE_TYPE) {
178             return session.createBytesMessage();
179         }
180         else if (messageType == JmsTransportConstants.TEXT_MESSAGE_TYPE) {
181             return session.createTextMessage();
182         }
183         else {
184             throw new IllegalArgumentException("Invalid message type [" + messageType + "].");
185         }
186 
187     }
188 
189 
190 }