View Javadoc

1   /*
2    * Copyright 2005-2012 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>
53   *     <table>
54   *         <tr><th><i>param-name</i></th><th><i>Description</i></th></tr>
55   *         <tr>
56   *             <td><tt>deliveryMode</tt></td>
57   *             <td>Indicates whether the request message is persistent or not. This may be <tt>PERSISTENT</tt> or
58   *             <tt>NON_PERSISTENT</tt>. See {@link MessageProducer#setDeliveryMode(int)}</td>
59   *         </tr>
60   *         <tr>
61   *             <td><tt>messageType</tt></td>
62   *             <td>The message type. This may be <tt>BINARY_MESSAGE</tt> (the default) or <tt>TEXT_MESSAGE</tt></td>
63   *         </tr>
64   *         <tr>
65   *             <td><tt>priority</tt></td>
66   *             <td>The JMS priority (0-9) associated with the request message. See
67   *             {@link MessageProducer#setPriority(int)}</td>
68   *         </tr>
69   *         <tr>
70   *             <td><tt>replyToName</tt></td>
71   *             <td>The name of the destination to which the response message must be sent, that will be resolved by
72   *             the {@link #getDestinationResolver() destination resolver}.</td>
73   *         </tr>
74   *         <tr>
75   *             <td><tt>timeToLive</tt></td>
76   *             <td>The lifetime, in milliseconds, of the request message. See
77   *             {@link MessageProducer#setTimeToLive(long)}</td>
78   *         </tr>
79   *     </table>
80   * </blockquote>
81   * <p/>
82   * If the <tt>replyToName</tt> is not set, a {@link Session#createTemporaryQueue() temporary queue} is used.
83   * <p/>
84   * This class uses {@link BytesMessage} messages by default, but can be configured to send {@link TextMessage} messages
85   * instead. <b>Note</b> that <code>BytesMessages</code> are preferred, since <code>TextMessages</code> do not support
86   * attachments and character encodings reliably.
87   * <p/>
88   * Some examples of JMS URIs are:
89   * <p/>
90   * <blockquote> <tt>jms:SomeQueue</tt><br> <tt>jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT</tt><br>
91   * <tt>jms:RequestQueue?replyToName=ResponseQueueName</tt><br> <tt>jms:Queue?messageType=TEXT_MESSAGE</blockquote>
92   *
93   * @author Arjen Poutsma
94   * @see <a href="http://tools.ietf.org/id/draft-merrick-jms-iri-00.txt">IRI Scheme for Java(tm) Message Service 1.0</a>
95   * @since 1.5.0
96   */
97  public class JmsMessageSender extends JmsDestinationAccessor implements WebServiceMessageSender {
98  
99      /** Default timeout for receive operations: -1 indicates a blocking receive without timeout. */
100     public static final long DEFAULT_RECEIVE_TIMEOUT = -1;
101 
102     /** Default encoding used to read fromn and write to {@link TextMessage} messages. */
103     public static final String DEFAULT_TEXT_MESSAGE_ENCODING = "UTF-8";
104 
105     private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
106 
107     private String textMessageEncoding = DEFAULT_TEXT_MESSAGE_ENCODING;
108 
109     private MessagePostProcessor postProcessor;
110 
111     /**
112      * Create a new <code>JmsMessageSender</code>
113      * <p/>
114      * <b>Note</b>: The ConnectionFactory has to be set before using the instance. This constructor can be used to
115      * prepare a JmsTemplate via a BeanFactory, typically setting the ConnectionFactory via {@link
116      * #setConnectionFactory(ConnectionFactory)}.
117      *
118      * @see #setConnectionFactory(ConnectionFactory)
119      */
120     public JmsMessageSender() {
121     }
122 
123     /**
124      * Create a new <code>JmsMessageSender</code>, given a ConnectionFactory.
125      *
126      * @param connectionFactory the ConnectionFactory to obtain Connections from
127      */
128     public JmsMessageSender(ConnectionFactory connectionFactory) {
129         setConnectionFactory(connectionFactory);
130     }
131 
132     /**
133      * Set the timeout to use for receive calls. The default is -1, which means no timeout.
134      *
135      * @see MessageConsumer#receive(long)
136      */
137     public void setReceiveTimeout(long receiveTimeout) {
138         this.receiveTimeout = receiveTimeout;
139     }
140 
141     /** Sets the encoding used to read from {@link TextMessage} messages. Defaults to <code>UTF-8</code>. */
142     public void setTextMessageEncoding(String textMessageEncoding) {
143         this.textMessageEncoding = textMessageEncoding;
144     }
145 
146     /**
147      * Sets the optional {@link MessagePostProcessor} to further modify outgoing messages after the XML contents has
148      * been set.
149      */
150     public void setPostProcessor(MessagePostProcessor postProcessor) {
151         this.postProcessor = postProcessor;
152     }
153 
154     public WebServiceConnection createConnection(URI uri) throws IOException {
155         Connection jmsConnection = null;
156         Session jmsSession = null;
157         try {
158             jmsConnection = createConnection();
159             jmsSession = createSession(jmsConnection);
160             Destination requestDestination = resolveRequestDestination(jmsSession, uri);
161             Message requestMessage = createRequestMessage(jmsSession, uri);
162             JmsSenderConnection wsConnection =
163                     new JmsSenderConnection(getConnectionFactory(), jmsConnection, jmsSession, requestDestination,
164                             requestMessage);
165             wsConnection.setDeliveryMode(JmsTransportUtils.getDeliveryMode(uri));
166             wsConnection.setPriority(JmsTransportUtils.getPriority(uri));
167             wsConnection.setReceiveTimeout(receiveTimeout);
168             wsConnection.setResponseDestination(resolveResponseDestination(jmsSession, uri));
169             wsConnection.setTimeToLive(JmsTransportUtils.getTimeToLive(uri));
170             wsConnection.setTextMessageEncoding(textMessageEncoding);
171             wsConnection.setSessionTransacted(isSessionTransacted());
172             wsConnection.setPostProcessor(postProcessor);
173             return wsConnection;
174         }
175         catch (JMSException ex) {
176             JmsUtils.closeSession(jmsSession);
177             ConnectionFactoryUtils.releaseConnection(jmsConnection, getConnectionFactory(), true);
178             throw new JmsTransportException(ex);
179         }
180     }
181 
182     public boolean supports(URI uri) {
183         return uri.getScheme().equals(JmsTransportConstants.JMS_URI_SCHEME);
184     }
185 
186     private Destination resolveRequestDestination(Session session, URI uri) throws JMSException {
187         return resolveDestinationName(session, JmsTransportUtils.getDestinationName(uri));
188     }
189 
190     private Destination resolveResponseDestination(Session session, URI uri) throws JMSException {
191         String destinationName = JmsTransportUtils.getReplyToName(uri);
192         return StringUtils.hasLength(destinationName) ? resolveDestinationName(session, destinationName) : null;
193     }
194 
195     private Message createRequestMessage(Session session, URI uri) throws JMSException {
196         int messageType = JmsTransportUtils.getMessageType(uri);
197         if (messageType == JmsTransportConstants.BYTES_MESSAGE_TYPE) {
198             return session.createBytesMessage();
199         }
200         else if (messageType == JmsTransportConstants.TEXT_MESSAGE_TYPE) {
201             return session.createTextMessage();
202         }
203         else {
204             throw new IllegalArgumentException("Invalid message type [" + messageType + "].");
205         }
206 
207     }
208 
209 
210 }