001/*
002 * Copyright 2002-2018 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.listener.endpoint;
018
019import javax.jms.Message;
020import javax.jms.MessageListener;
021import javax.resource.ResourceException;
022import javax.resource.spi.UnavailableException;
023
024import org.springframework.jca.endpoint.AbstractMessageEndpointFactory;
025
026/**
027 * JMS-specific implementation of the JCA 1.5
028 * {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
029 * providing transaction management capabilities for a JMS listener object
030 * (e.g. a {@link javax.jms.MessageListener} object).
031 *
032 * <p>Uses a static endpoint implementation, simply wrapping the
033 * specified message listener object and exposing all of its implemented
034 * interfaces on the endpoint instance.
035 *
036 * <p>Typically used with Spring's {@link JmsMessageEndpointManager},
037 * but not tied to it. As a consequence, this endpoint factory could
038 * also be used with programmatic endpoint management on a native
039 * {@link javax.resource.spi.ResourceAdapter} instance.
040 *
041 * @author Juergen Hoeller
042 * @author Stephane Nicoll
043 * @since 2.5
044 * @see #setMessageListener
045 * @see #setTransactionManager
046 * @see JmsMessageEndpointManager
047 */
048public class JmsMessageEndpointFactory extends AbstractMessageEndpointFactory  {
049
050        private MessageListener messageListener;
051
052
053        /**
054         * Set the JMS MessageListener for this endpoint.
055         */
056        public void setMessageListener(MessageListener messageListener) {
057                this.messageListener = messageListener;
058        }
059
060        /**
061         * Return the JMS MessageListener for this endpoint.
062         */
063        protected MessageListener getMessageListener() {
064                return this.messageListener;
065        }
066
067        /**
068         * Creates a concrete JMS message endpoint, internal to this factory.
069         */
070        @Override
071        protected AbstractMessageEndpoint createEndpointInternal() throws UnavailableException {
072                return new JmsMessageEndpoint();
073        }
074
075
076        /**
077         * Private inner class that implements the concrete JMS message endpoint.
078         */
079        private class JmsMessageEndpoint extends AbstractMessageEndpoint implements MessageListener {
080
081                @Override
082                public void onMessage(Message message) {
083                        Throwable endpointEx = null;
084                        boolean applyDeliveryCalls = !hasBeforeDeliveryBeenCalled();
085                        if (applyDeliveryCalls) {
086                                try {
087                                        beforeDelivery(null);
088                                }
089                                catch (ResourceException ex) {
090                                        throw new JmsResourceException(ex);
091                                }
092                        }
093                        try {
094                                messageListener.onMessage(message);
095                        }
096                        catch (RuntimeException ex) {
097                                endpointEx = ex;
098                                onEndpointException(ex);
099                                throw ex;
100                        }
101                        catch (Error err) {
102                                endpointEx = err;
103                                onEndpointException(err);
104                                throw err;
105                        }
106                        finally {
107                                if (applyDeliveryCalls) {
108                                        try {
109                                                afterDelivery();
110                                        }
111                                        catch (ResourceException ex) {
112                                                if (endpointEx == null) {
113                                                        throw new JmsResourceException(ex);
114                                                }
115                                        }
116                                }
117                        }
118                }
119
120                @Override
121                protected ClassLoader getEndpointClassLoader() {
122                        return getMessageListener().getClass().getClassLoader();
123                }
124        }
125
126
127        /**
128         * Internal exception thrown when a ResourceException has been encountered
129         * during the endpoint invocation.
130         * <p>Will only be used if the ResourceAdapter does not invoke the
131         * endpoint's {@code beforeDelivery} and {@code afterDelivery}
132         * directly, leaving it up to the concrete endpoint to apply those -
133         * and to handle any ResourceExceptions thrown from them.
134         */
135        @SuppressWarnings("serial")
136        public static class JmsResourceException extends RuntimeException {
137
138                public JmsResourceException(ResourceException cause) {
139                        super(cause);
140                }
141        }
142
143}