001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JMessageEndpointProxy.java 6673 2005-04-28 16:53:00Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.container;
025:
026: import java.lang.reflect.InvocationHandler;
027: import java.lang.reflect.InvocationTargetException;
028: import java.lang.reflect.Method;
029: import java.lang.reflect.Proxy;
030:
031: import javax.ejb.MessageDrivenBean;
032: import javax.ejb.EJBException;
033: import javax.resource.ResourceException;
034: import javax.resource.spi.IllegalStateException;
035: import javax.transaction.xa.XAResource;
036:
037: import org.objectweb.jonas_ejb.deployment.api.MethodDesc;
038:
039: import org.objectweb.transaction.jta.TransactionManager;
040: import org.objectweb.util.monolog.api.BasicLevel;
041:
042: /**
043: * Generic interposed class for Message Endpoints This class presents these
044: * interfaces, depending on object reached: MessageDrivenContext interface to
045: * the bean instance
046: * @author Philippe Coq, Philippe Durieux
047: * @author Christophe Ney (Easier Enhydra integration)
048: */
049: public class JMessageEndpointProxy implements InvocationHandler {
050:
051: protected JMdbEndpointFactory bf = null;
052:
053: protected JMessageEndpoint ep = null;
054:
055: protected MessageDrivenBean mdb = null;
056:
057: protected TransactionManager tm = null;
058:
059: boolean b4Delivery = false;
060:
061: int msgCount = 0;
062:
063: // RequestCtx associated to the thread
064: // Thread specific data
065: private transient static ThreadLocal requestCtx = new ThreadLocal();
066:
067: /**
068: * constructor
069: * @param bf The MDB Endpoint Factory
070: * @param sess The JMS Session
071: * @param mdb The Message Driven Bean
072: * @param thpool The Thread Pool
073: */
074: public JMessageEndpointProxy(JMdbEndpointFactory bf,
075: MessageDrivenBean mdb, JMessageEndpoint ep) {
076: this .bf = bf;
077: this .mdb = mdb;
078: this .ep = ep;
079: // keep these locally for efficiency.
080: tm = bf.getTransactionManager();
081: }
082:
083: // ------------------------------------------------------------------
084: // InvocationHandler implementation
085: // ------------------------------------------------------------------
086:
087: public Object invoke(Object obj, Method method, Object[] aobj)
088: throws Throwable, NoSuchMethodException, ResourceException,
089: Exception {
090: RequestCtx rctx = null;
091: String methodName = method.getName();
092: Object ret = null;
093: Throwable invokeEx = null;
094:
095: if (TraceEjb.isDebugJms()) {
096: TraceEjb.mdb.log(BasicLevel.DEBUG, "Calling " + methodName
097: + " on " + this );
098: }
099: if (ep.released) {
100: throw new IllegalStateException(
101: "Endpoint is in a released state and must be reactivated before using");
102: }
103: // Object methods
104: if ("equals".equals(methodName)) {
105: Object obj1 = null;
106: if (Proxy.isProxyClass(aobj[0].getClass())) {
107: obj1 = (Object) Proxy.getInvocationHandler(aobj[0]);
108: } else {
109: obj1 = aobj[0];
110: }
111: ret = new Boolean(this .equals(obj1));
112: } else if ("hashCode".equals(methodName)) {
113: ret = new Integer(this .hashCode());
114: } else if ("toString".equals(methodName)) {
115: ret = this .toString();
116: } else if ("afterDelivery".equals(methodName)) {
117: if (!b4Delivery) {
118: throw new IllegalStateException(methodName
119: + " called w/o call to beforeDelivery");
120: }
121: b4Delivery = false;
122: msgCount = 0;
123: rctx = (RequestCtx) requestCtx.get();
124: try {
125: if (rctx.mustCommit && ep.getXAResource() != null) {
126: rctx.currTx.delistResource(ep.getXAResource(),
127: XAResource.TMSUCCESS);
128: }
129: bf.postInvoke(rctx);
130: } catch (Exception e) {
131: TraceEjb.logger.log(BasicLevel.ERROR,
132: "exception on postInvoke: ", e);
133: throw new RuntimeException(e);
134: }
135: } else if ("beforeDelivery".equals(methodName)) {
136:
137: if (TraceEjb.isDebugJms()) {
138: TraceEjb.mdb.log(BasicLevel.DEBUG,
139: "beforeDelivery called");
140: }
141: if (b4Delivery) {
142: throw new IllegalStateException(methodName
143: + " called w/o call to afterDelivery");
144: }
145: if (bf.isTxBeanManaged()) {
146: throw new IllegalStateException(
147: methodName
148: + " cannot be called when using bean managed transactions");
149: }
150: if (tm.getTransaction() != null) {
151: throw new IllegalStateException(
152: methodName
153: + " cannot be called when using an imported transaction");
154: }
155: msgCount = 0;
156: // retrieve intented method name passed in parameter
157: Object obj1 = null;
158: if (Proxy.isProxyClass(aobj[0].getClass())) {
159: obj1 = (Object) Proxy.getInvocationHandler(aobj[0]);
160: } else {
161: obj1 = aobj[0];
162: }
163: Method intentedTargetMethod = (Method) obj1;
164: String intentedTargetMethodName = intentedTargetMethod
165: .getName();
166:
167: if (TraceEjb.isDebugJms()) {
168: TraceEjb.mdb.log(BasicLevel.DEBUG,
169: "intentedTargetMethodName="
170: + intentedTargetMethodName);
171: }
172: try {
173: if (TraceEjb.isDebugJms()) {
174: TraceEjb.mdb.log(BasicLevel.DEBUG,
175: "before preInvoke");
176: }
177: rctx = bf.preInvoke(getTxAttr(intentedTargetMethod));
178: // JCA 1.5 ?12.5.6 mentions that beforeDelivery and afterDelivery
179: // must be called from a single thread of control.
180: // so the RequestCtx is stored in thread specific data for next
181: // use in afterDelivery call and intended method call.
182: requestCtx.set(rctx);
183: b4Delivery = true;
184: if (rctx.mustCommit && ep.getXAResource() != null) {
185:
186: rctx.currTx.enlistResource(ep.getXAResource());
187: if (TraceEjb.isDebugJms()) {
188: TraceEjb.mdb.log(BasicLevel.DEBUG,
189: "enlistResource Ok");
190: }
191: }
192: } catch (Exception e) {
193: TraceEjb.logger.log(BasicLevel.ERROR,
194: "preInvoke failed: ", e);
195: throw new RuntimeException(e);
196: }
197: if (TraceEjb.isDebugJms()) {
198: TraceEjb.mdb.log(BasicLevel.DEBUG,
199: "beforeDelivery ended");
200: }
201: } else if ("release".equals(methodName)) {
202: bf.releaseEndpoint(ep);
203: } else {
204: msgCount++;
205:
206: if (!b4Delivery) {
207: boolean isImportedTx = tm.getTransaction() != null;
208: rctx = bf.preInvoke(getTxAttr(method));
209: // JCA 1.5 ?12.5.9
210: // if txRequired && imported tx, use the source managed Tx
211: // and ignore the XAResource
212: if (rctx.mustCommit) {
213: if (!isImportedTx) {
214:
215: if (ep.getXAResource() != null) {
216: rctx.currTx.enlistResource(ep
217: .getXAResource());
218: }
219: } else {
220: rctx.mustCommit = false;
221: }
222: }
223: bf.checkSecurity(null);
224: } else if (msgCount > 1) {
225: throw new IllegalStateException(
226: "Unable to deliver multiple messages");
227: } else {
228: rctx = (RequestCtx) requestCtx.get();
229: }
230: try {
231: if (TraceEjb.isDebugJms()) {
232: TraceEjb.mdb.log(BasicLevel.DEBUG, "Before invoke");
233: }
234: ret = method.invoke(mdb, aobj);
235: if (TraceEjb.isDebugJms()) {
236: TraceEjb.mdb.log(BasicLevel.DEBUG, "After invoke");
237: }
238: } catch (InvocationTargetException ite) {
239: Throwable t = ite.getTargetException();
240: if (t instanceof RuntimeException) {
241: if (rctx != null) {
242: rctx.sysExc = new EJBException(
243: (RuntimeException) t);
244: }
245: } else {
246: if (rctx != null) {
247: rctx.sysExc = t;
248: }
249: }
250: invokeEx = rctx.sysExc;
251: TraceEjb.logger.log(BasicLevel.ERROR,
252: "error thrown by an enterprise Bean", t);
253: } catch (Throwable ex) {
254: //ex.printStackTrace();
255: if (rctx != null) {
256: rctx.sysExc = ex;
257: }
258: invokeEx = ex;
259:
260: TraceEjb.logger.log(BasicLevel.ERROR,
261: "error thrown by an enterprise Bean", ex);
262: } finally {
263: if (!b4Delivery) {
264: try {
265: if (!bf.isTxBeanManaged() && rctx.mustCommit
266: && ep.getXAResource() != null) {
267: rctx.currTx.delistResource(ep
268: .getXAResource(),
269: XAResource.TMSUCCESS);
270: }
271: bf.postInvoke(rctx);
272: } catch (Exception e) {
273: TraceEjb.logger.log(BasicLevel.ERROR,
274: "exception on postInvoke: ", e);
275: if (invokeEx == null) {
276: invokeEx = e;
277: }
278: }
279: } else {
280: // Reset message count if not using after/before delivery
281: msgCount = 0;
282: }
283: }
284: }
285: if (invokeEx != null) {
286: TraceEjb.logger.log(BasicLevel.ERROR, "Exception raised: ",
287: invokeEx);
288: throw invokeEx;
289: }
290: if (TraceEjb.isDebugJms()) {
291: TraceEjb.mdb.log(BasicLevel.DEBUG, "ret=" + ret);
292: }
293: return ret;
294: }
295:
296: private int getTxAttr(Method m) {
297: int ret = MethodDesc.TX_NOT_SUPPORTED;
298: try {
299: ret = bf.isDeliveryTransacted(m) ? MethodDesc.TX_REQUIRED
300: : MethodDesc.TX_NOT_SUPPORTED;
301: } catch (NoSuchMethodException nsme) {
302: }
303: return ret;
304: }
305: }
|