001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jms.support;
018
019import javax.jms.Connection;
020import javax.jms.JMSException;
021import javax.jms.MessageConsumer;
022import javax.jms.MessageProducer;
023import javax.jms.QueueBrowser;
024import javax.jms.QueueRequestor;
025import javax.jms.Session;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030import org.springframework.jms.InvalidClientIDException;
031import org.springframework.jms.InvalidDestinationException;
032import org.springframework.jms.InvalidSelectorException;
033import org.springframework.jms.JmsException;
034import org.springframework.jms.JmsSecurityException;
035import org.springframework.jms.MessageEOFException;
036import org.springframework.jms.MessageFormatException;
037import org.springframework.jms.MessageNotReadableException;
038import org.springframework.jms.MessageNotWriteableException;
039import org.springframework.jms.ResourceAllocationException;
040import org.springframework.jms.TransactionInProgressException;
041import org.springframework.jms.TransactionRolledBackException;
042import org.springframework.jms.UncategorizedJmsException;
043import org.springframework.util.Assert;
044
045/**
046 * Generic utility methods for working with JMS. Mainly for internal use
047 * within the framework, but also useful for custom JMS access code.
048 *
049 * @author Juergen Hoeller
050 * @since 1.1
051 */
052public abstract class JmsUtils {
053
054        private static final Log logger = LogFactory.getLog(JmsUtils.class);
055
056
057        /**
058         * Close the given JMS Connection and ignore any thrown exception.
059         * This is useful for typical {@code finally} blocks in manual JMS code.
060         * @param con the JMS Connection to close (may be {@code null})
061         */
062        public static void closeConnection(Connection con) {
063                closeConnection(con, false);
064        }
065
066        /**
067         * Close the given JMS Connection and ignore any thrown exception.
068         * This is useful for typical {@code finally} blocks in manual JMS code.
069         * @param con the JMS Connection to close (may be {@code null})
070         * @param stop whether to call {@code stop()} before closing
071         */
072        public static void closeConnection(Connection con, boolean stop) {
073                if (con != null) {
074                        try {
075                                if (stop) {
076                                        try {
077                                                con.stop();
078                                        }
079                                        finally {
080                                                con.close();
081                                        }
082                                }
083                                else {
084                                        con.close();
085                                }
086                        }
087                        catch (javax.jms.IllegalStateException ex) {
088                                logger.debug("Ignoring Connection state exception - assuming already closed: " + ex);
089                        }
090                        catch (JMSException ex) {
091                                logger.debug("Could not close JMS Connection", ex);
092                        }
093                        catch (Throwable ex) {
094                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
095                                logger.debug("Unexpected exception on closing JMS Connection", ex);
096                        }
097                }
098        }
099
100        /**
101         * Close the given JMS Session and ignore any thrown exception.
102         * This is useful for typical {@code finally} blocks in manual JMS code.
103         * @param session the JMS Session to close (may be {@code null})
104         */
105        public static void closeSession(Session session) {
106                if (session != null) {
107                        try {
108                                session.close();
109                        }
110                        catch (JMSException ex) {
111                                logger.trace("Could not close JMS Session", ex);
112                        }
113                        catch (Throwable ex) {
114                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
115                                logger.trace("Unexpected exception on closing JMS Session", ex);
116                        }
117                }
118        }
119
120        /**
121         * Close the given JMS MessageProducer and ignore any thrown exception.
122         * This is useful for typical {@code finally} blocks in manual JMS code.
123         * @param producer the JMS MessageProducer to close (may be {@code null})
124         */
125        public static void closeMessageProducer(MessageProducer producer) {
126                if (producer != null) {
127                        try {
128                                producer.close();
129                        }
130                        catch (JMSException ex) {
131                                logger.trace("Could not close JMS MessageProducer", ex);
132                        }
133                        catch (Throwable ex) {
134                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
135                                logger.trace("Unexpected exception on closing JMS MessageProducer", ex);
136                        }
137                }
138        }
139
140        /**
141         * Close the given JMS MessageConsumer and ignore any thrown exception.
142         * This is useful for typical {@code finally} blocks in manual JMS code.
143         * @param consumer the JMS MessageConsumer to close (may be {@code null})
144         */
145        public static void closeMessageConsumer(MessageConsumer consumer) {
146                if (consumer != null) {
147                        // Clear interruptions to ensure that the consumer closes successfully...
148                        // (working around misbehaving JMS providers such as ActiveMQ)
149                        boolean wasInterrupted = Thread.interrupted();
150                        try {
151                                consumer.close();
152                        }
153                        catch (JMSException ex) {
154                                logger.trace("Could not close JMS MessageConsumer", ex);
155                        }
156                        catch (Throwable ex) {
157                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
158                                logger.trace("Unexpected exception on closing JMS MessageConsumer", ex);
159                        }
160                        finally {
161                                if (wasInterrupted) {
162                                        // Reset the interrupted flag as it was before.
163                                        Thread.currentThread().interrupt();
164                                }
165                        }
166                }
167        }
168
169        /**
170         * Close the given JMS QueueBrowser and ignore any thrown exception.
171         * This is useful for typical {@code finally} blocks in manual JMS code.
172         * @param browser the JMS QueueBrowser to close (may be {@code null})
173         */
174        public static void closeQueueBrowser(QueueBrowser browser) {
175                if (browser != null) {
176                        try {
177                                browser.close();
178                        }
179                        catch (JMSException ex) {
180                                logger.trace("Could not close JMS QueueBrowser", ex);
181                        }
182                        catch (Throwable ex) {
183                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
184                                logger.trace("Unexpected exception on closing JMS QueueBrowser", ex);
185                        }
186                }
187        }
188
189        /**
190         * Close the given JMS QueueRequestor and ignore any thrown exception.
191         * This is useful for typical {@code finally} blocks in manual JMS code.
192         * @param requestor the JMS QueueRequestor to close (may be {@code null})
193         */
194        public static void closeQueueRequestor(QueueRequestor requestor) {
195                if (requestor != null) {
196                        try {
197                                requestor.close();
198                        }
199                        catch (JMSException ex) {
200                                logger.trace("Could not close JMS QueueRequestor", ex);
201                        }
202                        catch (Throwable ex) {
203                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
204                                logger.trace("Unexpected exception on closing JMS QueueRequestor", ex);
205                        }
206                }
207        }
208
209        /**
210         * Commit the Session if not within a JTA transaction.
211         * @param session the JMS Session to commit
212         * @throws JMSException if committing failed
213         */
214        public static void commitIfNecessary(Session session) throws JMSException {
215                Assert.notNull(session, "Session must not be null");
216                try {
217                        session.commit();
218                }
219                catch (javax.jms.TransactionInProgressException ex) {
220                        // Ignore -> can only happen in case of a JTA transaction.
221                }
222                catch (javax.jms.IllegalStateException ex) {
223                        // Ignore -> can only happen in case of a JTA transaction.
224                }
225        }
226
227        /**
228         * Rollback the Session if not within a JTA transaction.
229         * @param session the JMS Session to rollback
230         * @throws JMSException if committing failed
231         */
232        public static void rollbackIfNecessary(Session session) throws JMSException {
233                Assert.notNull(session, "Session must not be null");
234                try {
235                        session.rollback();
236                }
237                catch (javax.jms.TransactionInProgressException ex) {
238                        // Ignore -> can only happen in case of a JTA transaction.
239                }
240                catch (javax.jms.IllegalStateException ex) {
241                        // Ignore -> can only happen in case of a JTA transaction.
242                }
243        }
244
245        /**
246         * Build a descriptive exception message for the given JMSException,
247         * incorporating a linked exception's message if appropriate.
248         * @param ex the JMSException to build a message for
249         * @return the descriptive message String
250         * @see javax.jms.JMSException#getLinkedException()
251         */
252        public static String buildExceptionMessage(JMSException ex) {
253                String message = ex.getMessage();
254                Exception linkedEx = ex.getLinkedException();
255                if (linkedEx != null) {
256                        if (message == null) {
257                                message = linkedEx.toString();
258                        }
259                        else {
260                                String linkedMessage = linkedEx.getMessage();
261                                if (linkedMessage != null && !message.contains(linkedMessage)) {
262                                        message = message + "; nested exception is " + linkedEx;
263                                }
264                        }
265                }
266                return message;
267        }
268
269        /**
270         * Convert the specified checked {@link javax.jms.JMSException JMSException} to a
271         * Spring runtime {@link org.springframework.jms.JmsException JmsException} equivalent.
272         * @param ex the original checked JMSException to convert
273         * @return the Spring runtime JmsException wrapping the given exception
274         */
275        public static JmsException convertJmsAccessException(JMSException ex) {
276                Assert.notNull(ex, "JMSException must not be null");
277
278                if (ex instanceof javax.jms.IllegalStateException) {
279                        return new org.springframework.jms.IllegalStateException((javax.jms.IllegalStateException) ex);
280                }
281                if (ex instanceof javax.jms.InvalidClientIDException) {
282                        return new InvalidClientIDException((javax.jms.InvalidClientIDException) ex);
283                }
284                if (ex instanceof javax.jms.InvalidDestinationException) {
285                        return new InvalidDestinationException((javax.jms.InvalidDestinationException) ex);
286                }
287                if (ex instanceof javax.jms.InvalidSelectorException) {
288                        return new InvalidSelectorException((javax.jms.InvalidSelectorException) ex);
289                }
290                if (ex instanceof javax.jms.JMSSecurityException) {
291                        return new JmsSecurityException((javax.jms.JMSSecurityException) ex);
292                }
293                if (ex instanceof javax.jms.MessageEOFException) {
294                        return new MessageEOFException((javax.jms.MessageEOFException) ex);
295                }
296                if (ex instanceof javax.jms.MessageFormatException) {
297                        return new MessageFormatException((javax.jms.MessageFormatException) ex);
298                }
299                if (ex instanceof javax.jms.MessageNotReadableException) {
300                        return new MessageNotReadableException((javax.jms.MessageNotReadableException) ex);
301                }
302                if (ex instanceof javax.jms.MessageNotWriteableException) {
303                        return new MessageNotWriteableException((javax.jms.MessageNotWriteableException) ex);
304                }
305                if (ex instanceof javax.jms.ResourceAllocationException) {
306                        return new ResourceAllocationException((javax.jms.ResourceAllocationException) ex);
307                }
308                if (ex instanceof javax.jms.TransactionInProgressException) {
309                        return new TransactionInProgressException((javax.jms.TransactionInProgressException) ex);
310                }
311                if (ex instanceof javax.jms.TransactionRolledBackException) {
312                        return new TransactionRolledBackException((javax.jms.TransactionRolledBackException) ex);
313                }
314
315                // fallback
316                return new UncategorizedJmsException(ex);
317        }
318
319}