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