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.jca.endpoint;
018
019import javax.resource.ResourceException;
020import javax.resource.spi.UnavailableException;
021import javax.resource.spi.endpoint.MessageEndpoint;
022import javax.transaction.xa.XAResource;
023
024import org.aopalliance.intercept.MethodInterceptor;
025import org.aopalliance.intercept.MethodInvocation;
026
027import org.springframework.aop.framework.ProxyFactory;
028import org.springframework.aop.support.DelegatingIntroductionInterceptor;
029import org.springframework.util.ReflectionUtils;
030
031/**
032 * Generic implementation of the JCA 1.5
033 * {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
034 * providing transaction management capabilities for any kind of message
035 * listener object (e.g. {@link javax.jms.MessageListener} objects or
036 * {@link javax.resource.cci.MessageListener} objects.
037 *
038 * <p>Uses AOP proxies for concrete endpoint instances, simply wrapping
039 * the specified message listener object and exposing all of its implemented
040 * interfaces on the endpoint instance.
041 *
042 * <p>Typically used with Spring's {@link GenericMessageEndpointManager},
043 * but not tied to it. As a consequence, this endpoint factory could
044 * also be used with programmatic endpoint management on a native
045 * {@link javax.resource.spi.ResourceAdapter} instance.
046 *
047 * @author Juergen Hoeller
048 * @since 2.5
049 * @see #setMessageListener
050 * @see #setTransactionManager
051 * @see GenericMessageEndpointManager
052 */
053public class GenericMessageEndpointFactory extends AbstractMessageEndpointFactory {
054
055        private Object messageListener;
056
057
058        /**
059         * Specify the message listener object that the endpoint should expose
060         * (e.g. a {@link javax.jms.MessageListener} objects or
061         * {@link javax.resource.cci.MessageListener} implementation).
062         */
063        public void setMessageListener(Object messageListener) {
064                this.messageListener = messageListener;
065        }
066
067        /**
068         * Wrap each concrete endpoint instance with an AOP proxy,
069         * exposing the message listener's interfaces as well as the
070         * endpoint SPI through an AOP introduction.
071         */
072        @Override
073        public MessageEndpoint createEndpoint(XAResource xaResource) throws UnavailableException {
074                GenericMessageEndpoint endpoint = (GenericMessageEndpoint) super.createEndpoint(xaResource);
075                ProxyFactory proxyFactory = new ProxyFactory(this.messageListener);
076                DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(endpoint);
077                introduction.suppressInterface(MethodInterceptor.class);
078                proxyFactory.addAdvice(introduction);
079                return (MessageEndpoint) proxyFactory.getProxy();
080        }
081
082        /**
083         * Creates a concrete generic message endpoint, internal to this factory.
084         */
085        @Override
086        protected AbstractMessageEndpoint createEndpointInternal() throws UnavailableException {
087                return new GenericMessageEndpoint();
088        }
089
090
091        /**
092         * Private inner class that implements the concrete generic message endpoint,
093         * as an AOP Alliance MethodInterceptor that will be invoked by a proxy.
094         */
095        private class GenericMessageEndpoint extends AbstractMessageEndpoint implements MethodInterceptor {
096
097                @Override
098                public Object invoke(MethodInvocation methodInvocation) throws Throwable {
099                        Throwable endpointEx = null;
100                        boolean applyDeliveryCalls = !hasBeforeDeliveryBeenCalled();
101                        if (applyDeliveryCalls) {
102                                try {
103                                        beforeDelivery(null);
104                                }
105                                catch (ResourceException ex) {
106                                        throw adaptExceptionIfNecessary(methodInvocation, ex);
107                                }
108                        }
109                        try {
110                                return methodInvocation.proceed();
111                        }
112                        catch (Throwable ex) {
113                                endpointEx = ex;
114                                onEndpointException(ex);
115                                throw ex;
116                        }
117                        finally {
118                                if (applyDeliveryCalls) {
119                                        try {
120                                                afterDelivery();
121                                        }
122                                        catch (ResourceException ex) {
123                                                if (endpointEx == null) {
124                                                        throw adaptExceptionIfNecessary(methodInvocation, ex);
125                                                }
126                                        }
127                                }
128                        }
129                }
130
131                private Exception adaptExceptionIfNecessary(MethodInvocation methodInvocation, ResourceException ex) {
132                        if (ReflectionUtils.declaresException(methodInvocation.getMethod(), ex.getClass())) {
133                                return ex;
134                        }
135                        else {
136                                return new InternalResourceException(ex);
137                        }
138                }
139
140                @Override
141                protected ClassLoader getEndpointClassLoader() {
142                        return messageListener.getClass().getClassLoader();
143                }
144        }
145
146
147        /**
148         * Internal exception thrown when a ResourceException has been encountered
149         * during the endpoint invocation.
150         * <p>Will only be used if the ResourceAdapter does not invoke the
151         * endpoint's {@code beforeDelivery} and {@code afterDelivery}
152         * directly, leaving it up to the concrete endpoint to apply those -
153         * and to handle any ResourceExceptions thrown from them.
154         */
155        @SuppressWarnings("serial")
156        public static class InternalResourceException extends RuntimeException {
157
158                protected InternalResourceException(ResourceException cause) {
159                        super(cause);
160                }
161        }
162
163}