001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.jca.endpoint;
018:
019: import javax.resource.ResourceException;
020: import javax.resource.spi.UnavailableException;
021: import javax.resource.spi.endpoint.MessageEndpoint;
022: import javax.transaction.xa.XAResource;
023:
024: import org.aopalliance.intercept.MethodInterceptor;
025: import org.aopalliance.intercept.MethodInvocation;
026:
027: import org.springframework.aop.framework.ProxyFactory;
028: import org.springframework.aop.support.DelegatingIntroductionInterceptor;
029: import 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: */
053: public class GenericMessageEndpointFactory extends
054: AbstractMessageEndpointFactory {
055:
056: private Object messageListener;
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: public MessageEndpoint createEndpoint(XAResource xaResource)
073: throws UnavailableException {
074: GenericMessageEndpoint endpoint = (GenericMessageEndpoint) super
075: .createEndpoint(xaResource);
076: ProxyFactory proxyFactory = new ProxyFactory(
077: this .messageListener);
078: DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(
079: endpoint);
080: introduction.suppressInterface(MethodInterceptor.class);
081: proxyFactory.addAdvice(introduction);
082: return (MessageEndpoint) proxyFactory.getProxy();
083: }
084:
085: /**
086: * Creates a concrete generic message endpoint, internal to this factory.
087: */
088: protected AbstractMessageEndpoint createEndpointInternal()
089: throws UnavailableException {
090: return new GenericMessageEndpoint();
091: }
092:
093: /**
094: * Private inner class that implements the concrete generic message endpoint,
095: * as an AOP Alliance MethodInterceptor that will be invoked by a proxy.
096: */
097: private class GenericMessageEndpoint extends
098: AbstractMessageEndpoint implements MethodInterceptor {
099:
100: public Object invoke(MethodInvocation methodInvocation)
101: throws Throwable {
102: boolean applyDeliveryCalls = !hasBeforeDeliveryBeenCalled();
103: if (applyDeliveryCalls) {
104: try {
105: beforeDelivery(null);
106: } catch (ResourceException ex) {
107: if (ReflectionUtils
108: .declaresException(methodInvocation
109: .getMethod(), ex.getClass())) {
110: throw ex;
111: } else {
112: throw new InternalResourceException(ex);
113: }
114: }
115: }
116: try {
117: return methodInvocation.proceed();
118: } catch (Throwable ex) {
119: onEndpointException(ex);
120: throw ex;
121: } finally {
122: if (applyDeliveryCalls) {
123: try {
124: afterDelivery();
125: } catch (ResourceException ex) {
126: if (ReflectionUtils.declaresException(
127: methodInvocation.getMethod(), ex
128: .getClass())) {
129: throw ex;
130: } else {
131: throw new InternalResourceException(ex);
132: }
133: }
134: }
135: }
136: }
137:
138: protected ClassLoader getEndpointClassLoader() {
139: return messageListener.getClass().getClassLoader();
140: }
141: }
142:
143: /**
144: * Internal exception thrown when a ResourceExeption has been encountered
145: * during the endpoint invocation.
146: * <p>Will only be used if the ResourceAdapter does not invoke the
147: * endpoint's <code>beforeDelivery</code> and <code>afterDelivery</code>
148: * directly, leavng it up to the concrete endpoint to apply those -
149: * and to handle any ResourceExceptions thrown from them.
150: */
151: public static class InternalResourceException extends
152: RuntimeException {
153:
154: protected InternalResourceException(ResourceException cause) {
155: super(cause);
156: }
157: }
158:
159: }
|