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}