001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.openejb.core.mdb;
018:
019: import org.apache.openejb.ApplicationException;
020: import org.apache.openejb.SystemException;
021: import org.apache.openejb.core.CoreDeploymentInfo;
022: import org.apache.openejb.core.mdb.Instance;
023:
024: import javax.ejb.EJBException;
025: import javax.resource.spi.ApplicationServerInternalException;
026: import javax.resource.spi.UnavailableException;
027: import javax.resource.spi.endpoint.MessageEndpoint;
028: import javax.transaction.xa.XAResource;
029: import java.lang.reflect.InvocationHandler;
030: import java.lang.reflect.Method;
031: import java.util.Arrays;
032:
033: public class EndpointHandler implements InvocationHandler,
034: MessageEndpoint {
035: private static enum State {
036: /**
037: * The handler has been initialized and is ready for invoation
038: */
039: NONE,
040:
041: /**
042: * The beforeDelivery method has been called, and the next method called must be a message delivery method
043: * or release.
044: */
045: BEFORE_CALLED,
046:
047: /**
048: * The message delivery method has been called successfully, and the next method called must be
049: * another message delivery method, afterDelivery, or release.
050: */
051: METHOD_CALLED,
052:
053: /**
054: * The message delivery threw a system exception, and the next method called must be afterDelivery
055: * or release. This state notified the afterDelivery method that the instace must be replaced with a new
056: * instance.
057: */
058: SYSTEM_EXCEPTION,
059:
060: /**
061: * This message endpoint handler has been released and can no longer be used.
062: */
063: RELEASED
064: }
065:
066: private final MdbContainer container;
067: private final CoreDeploymentInfo deployment;
068: private final MdbInstanceFactory instanceFactory;
069: private final XAResource xaResource;
070:
071: private State state = State.NONE;
072: private Object instance;
073:
074: public EndpointHandler(MdbContainer container,
075: CoreDeploymentInfo deployment,
076: MdbInstanceFactory instanceFactory, XAResource xaResource)
077: throws UnavailableException {
078: this .container = container;
079: this .deployment = deployment;
080: this .instanceFactory = instanceFactory;
081: this .xaResource = xaResource;
082: instance = instanceFactory.createInstance(false);
083: }
084:
085: // private static void logTx() {
086: // TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class);
087: // Transaction transaction = null;
088: // String status = "ERROR";
089: // try {
090: // transaction = transactionManager.getTransaction();
091: // int txStatus;
092: // if (transaction != null) {
093: // txStatus = transaction.getStatus();
094: // } else {
095: // txStatus = Status.STATUS_NO_TRANSACTION;
096: // }
097: // switch (txStatus) {
098: // case Status.STATUS_ACTIVE:
099: // status = "STATUS_ACTIVE";
100: // break;
101: // case Status.STATUS_MARKED_ROLLBACK:
102: // status = "MARKED_ROLLBACK";
103: // break;
104: // case Status.STATUS_PREPARED:
105: // status = "PREPARED";
106: // break;
107: // case Status.STATUS_COMMITTED:
108: // status = "COMMITTED";
109: // break;
110: // case Status.STATUS_ROLLEDBACK:
111: // status = "ROLLEDBACK";
112: // break;
113: // case Status.STATUS_UNKNOWN:
114: // status = "UNKNOWN";
115: // break;
116: // case Status.STATUS_NO_TRANSACTION:
117: // status = "NO_TRANSACTION";
118: // break;
119: // case Status.STATUS_PREPARING:
120: // status = "PREPARING";
121: // break;
122: // case Status.STATUS_COMMITTING:
123: // status = "COMMITTING";
124: // break;
125: // case Status.STATUS_ROLLING_BACK:
126: // status = "ROLLING_BACK";
127: // break;
128: // default:
129: // status = "UNKNOWN " + txStatus;
130: // }
131: // } catch (javax.transaction.SystemException e) {
132: // }
133: // System.out.println("\n" +
134: // "***************************************\n" +
135: // "transaction " + transaction + "\n" +
136: // " status " + status + "\n" +
137: // "***************************************\n\n");
138: //
139: // }
140:
141: public Object invoke(Object proxy, Method method, Object[] args)
142: throws Throwable {
143: // System.out.println("\n" +
144: // "***************************************\n" +
145: // "Endpoint invoked " + method + "\n" +
146: // "***************************************\n\n");
147:
148: String methodName = method.getName();
149: Class<?>[] parameterTypes = method.getParameterTypes();
150:
151: if (method.getDeclaringClass() == Object.class) {
152: if ("toString".equals(methodName)
153: && parameterTypes.length == 0) {
154: return toString();
155: } else if ("equals".equals(methodName)
156: && parameterTypes.length == 1) {
157: return equals(args[0]);
158: } else if ("hashCode".equals(methodName)
159: && parameterTypes.length == 0) {
160: return hashCode();
161: } else {
162: throw new UnsupportedOperationException(
163: "Unkown method: " + method);
164: }
165: }
166:
167: // try {
168: if ("beforeDelivery".equals(methodName)
169: && Arrays.deepEquals(new Class[] { Method.class },
170: parameterTypes)) {
171: beforeDelivery((Method) args[0]);
172: return null;
173: } else if ("afterDelivery".equals(methodName)
174: && parameterTypes.length == 0) {
175: afterDelivery();
176: return null;
177: } else if ("release".equals(methodName)
178: && parameterTypes.length == 0) {
179: release();
180: return null;
181: } else {
182: Object value = deliverMessage(method, args);
183: return value;
184: }
185: // } finally { logTx(); }
186: }
187:
188: public void beforeDelivery(Method method)
189: throws ApplicationServerInternalException {
190: // verify current state
191: switch (state) {
192: case RELEASED:
193: throw new IllegalStateException(
194: "Message endpoint factory has been released");
195: case BEFORE_CALLED:
196: throw new IllegalStateException(
197: "beforeDelivery can not be called again until message is delivered and afterDelivery is called");
198: case METHOD_CALLED:
199: case SYSTEM_EXCEPTION:
200: throw new IllegalStateException(
201: "The last message delivery must be completed with an afterDeliver before beforeDeliver can be called again");
202: }
203:
204: // call beforeDelivery on the container
205: try {
206: container.beforeDelivery(deployment, instance, method,
207: xaResource);
208: } catch (SystemException se) {
209: Throwable throwable = (se.getRootCause() != null) ? se
210: .getRootCause() : se;
211: throw new ApplicationServerInternalException(throwable);
212: }
213:
214: // before completed successfully we are now ready to invoke bean
215: state = State.BEFORE_CALLED;
216: }
217:
218: public Object deliverMessage(Method method, Object[] args)
219: throws Throwable {
220:
221: boolean callBeforeAfter = false;
222:
223: // verify current state
224: switch (state) {
225: case NONE:
226: try {
227: beforeDelivery(method);
228: } catch (ApplicationServerInternalException e) {
229: throw (EJBException) new EJBException().initCause(e
230: .getCause());
231: }
232: callBeforeAfter = true;
233: state = State.METHOD_CALLED;
234: break;
235: case BEFORE_CALLED:
236: state = State.METHOD_CALLED;
237: break;
238: case RELEASED:
239: throw new IllegalStateException(
240: "Message endpoint factory has been released");
241: case METHOD_CALLED:
242: case SYSTEM_EXCEPTION:
243: throw new IllegalStateException(
244: "The last message delivery must be completed with an afterDeliver before another message can be delivered");
245: }
246:
247: Throwable throwable = null;
248: Object value = null;
249: try {
250: // deliver the message
251: value = container.invoke(instance, method, args);
252: } catch (SystemException se) {
253: throwable = (se.getRootCause() != null) ? se.getRootCause()
254: : se;
255: state = State.SYSTEM_EXCEPTION;
256: } catch (ApplicationException ae) {
257: throwable = (ae.getRootCause() != null) ? ae.getRootCause()
258: : ae;
259: } finally {
260: // if the adapter is not using before/after, we must call afterDelivery to clean up
261: if (callBeforeAfter) {
262: try {
263: afterDelivery();
264: } catch (ApplicationServerInternalException e) {
265: throwable = throwable == null ? e.getCause()
266: : throwable;
267: } catch (UnavailableException e) {
268: throwable = throwable == null ? e : throwable;
269: }
270: }
271: }
272:
273: if (throwable != null) {
274: throwable.printStackTrace();
275: if (isValidException(method, throwable)) {
276: throw throwable;
277: } else {
278: throw new EJBException().initCause(throwable);
279: }
280: }
281: return value;
282: }
283:
284: public void afterDelivery()
285: throws ApplicationServerInternalException,
286: UnavailableException {
287: // verify current state
288: switch (state) {
289: case RELEASED:
290: throw new IllegalStateException(
291: "Message endpoint factory has been released");
292: case BEFORE_CALLED:
293: throw new IllegalStateException(
294: "Exactally one message must be delivered between beforeDelivery and afterDelivery");
295: case NONE:
296: throw new IllegalStateException(
297: "afterDelivery may only be called if message delivery began with a beforeDelivery call");
298: }
299:
300: // call afterDelivery on the container
301: boolean exceptionThrown = false;
302: try {
303: container.afterDelivery(instance);
304: } catch (SystemException se) {
305: exceptionThrown = true;
306:
307: Throwable throwable = (se.getRootCause() != null) ? se
308: .getRootCause() : se;
309: throwable.printStackTrace();
310: throw new ApplicationServerInternalException(throwable);
311: } finally {
312: if (state == State.SYSTEM_EXCEPTION) {
313: recreateInstance(exceptionThrown);
314: }
315: // we are now in the default NONE state
316: state = State.NONE;
317: }
318: }
319:
320: private void recreateInstance(boolean exceptionAlreadyThrown)
321: throws UnavailableException {
322: try {
323: instance = instanceFactory.recreateInstance(instance);
324: } catch (UnavailableException e) {
325: // an error occured wile attempting to create the replacement instance
326: // this endpoint is now failed
327: state = State.RELEASED;
328:
329: // if bean threw an exception, do not override that exception
330: if (!exceptionAlreadyThrown) {
331: throw e;
332: }
333: }
334: }
335:
336: public void release() {
337: if (state == State.RELEASED)
338: return;
339: state = State.RELEASED;
340:
341: // notify the container
342: try {
343: container.release(deployment, instance);
344: } finally {
345: instanceFactory.freeInstance((Instance) instance, false);
346: instance = null;
347: }
348: }
349:
350: private boolean isValidException(Method method, Throwable throwable) {
351: if (throwable instanceof RuntimeException
352: || throwable instanceof Error)
353: return true;
354:
355: Class<?>[] exceptionTypes = method.getExceptionTypes();
356: for (Class<?> exceptionType : exceptionTypes) {
357: if (exceptionType.isInstance(throwable)) {
358: return true;
359: }
360: }
361: return false;
362: }
363: }
|