001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins;
023:
024: import java.io.PrintWriter;
025: import java.io.StringWriter;
026: import java.rmi.*;
027: import java.security.GeneralSecurityException;
028:
029: import javax.ejb.EJBException;
030: import javax.ejb.NoSuchEntityException;
031: import javax.ejb.NoSuchObjectLocalException;
032: import javax.ejb.TransactionRolledbackLocalException;
033: import javax.ejb.AccessLocalException;
034: import javax.transaction.TransactionRolledbackException;
035:
036: import org.jboss.invocation.Invocation;
037: import org.jboss.invocation.InvocationType;
038: import org.jboss.invocation.JBossLazyUnmarshallingException;
039: import org.jboss.metadata.BeanMetaData;
040:
041: import org.jboss.tm.JBossTransactionRolledbackException;
042: import org.jboss.tm.JBossTransactionRolledbackLocalException;
043:
044: /**
045: * An interceptor used to log all invocations. It also handles any
046: * unexpected exceptions.
047: *
048: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard �berg</a>
049: * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
050: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
051: * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
052: * @version $Revision: 57209 $
053: */
054: public class LogInterceptor extends AbstractInterceptor {
055: // Static --------------------------------------------------------
056:
057: // Attributes ----------------------------------------------------
058: protected String ejbName;
059: protected boolean callLogging;
060:
061: // Constructors --------------------------------------------------
062:
063: // Public --------------------------------------------------------
064:
065: // Container implementation --------------------------------------
066: public void create() throws Exception {
067: super .start();
068:
069: BeanMetaData md = getContainer().getBeanMetaData();
070: ejbName = md.getEjbName();
071:
072: // Should we log call details
073: callLogging = md.getContainerConfiguration().getCallLogging();
074: }
075:
076: /**
077: * This method logs the method, calls the next invoker, and handles
078: * any exception.
079: *
080: * @param invocation contain all infomation necessary to carry out the
081: * invocation
082: * @return the return value of the invocation
083: * @exception Exception if an exception during the invocation
084: */
085: public Object invokeHome(Invocation invocation) throws Exception {
086: String methodName;
087: if (invocation.getMethod() != null) {
088: methodName = invocation.getMethod().getName();
089: } else {
090: methodName = "<no method>";
091: }
092:
093: boolean trace = log.isTraceEnabled();
094: if (trace) {
095: log.trace("Start method=" + methodName);
096: }
097:
098: // Log call details
099: if (callLogging) {
100: StringBuffer str = new StringBuffer("InvokeHome: ");
101: str.append(methodName);
102: str.append("(");
103: Object[] args = invocation.getArguments();
104: if (args != null) {
105: for (int i = 0; i < args.length; i++) {
106: if (i > 0) {
107: str.append(",");
108: }
109: str.append(args[i]);
110: }
111: }
112: str.append(")");
113: log.debug(str.toString());
114: }
115:
116: try {
117: return getNext().invokeHome(invocation);
118: } catch (Throwable e) {
119: throw handleException(e, invocation);
120: } finally {
121: if (trace) {
122: log.trace("End method=" + methodName);
123: }
124: }
125: }
126:
127: /**
128: * This method logs the method, calls the next invoker, and handles
129: * any exception.
130: *
131: * @param invocation contain all infomation necessary to carry out the
132: * invocation
133: * @return the return value of the invocation
134: * @exception Exception if an exception during the invocation
135: */
136: public Object invoke(Invocation invocation) throws Exception {
137: String methodName;
138: if (invocation.getMethod() != null) {
139: methodName = invocation.getMethod().getName();
140: } else {
141: methodName = "<no method>";
142: }
143:
144: boolean trace = log.isTraceEnabled();
145: if (trace) {
146: log.trace("Start method=" + methodName);
147: }
148:
149: // Log call details
150: if (callLogging) {
151: StringBuffer str = new StringBuffer("Invoke: ");
152: if (invocation.getId() != null) {
153: str.append("[");
154: str.append(invocation.getId().toString());
155: str.append("] ");
156: }
157: str.append(methodName);
158: str.append("(");
159: Object[] args = invocation.getArguments();
160: if (args != null) {
161: for (int i = 0; i < args.length; i++) {
162: if (i > 0) {
163: str.append(",");
164: }
165: str.append(args[i]);
166: }
167: }
168: str.append(")");
169: log.debug(str.toString());
170: }
171:
172: try {
173: return getNext().invoke(invocation);
174: } catch (Throwable e) {
175: throw handleException(e, invocation);
176: } finally {
177: if (trace) {
178: log.trace("End method=" + methodName);
179: }
180: }
181: }
182:
183: // Private -------------------------------------------------------
184:
185: /**
186: PLEASE DO NOT CHANGE THIS CODE WITHOUT LOOKING AT __ALL__ OF IT TO MAKE ___SURE___
187: YOUR CHANGES ARE NECESSARY AND DO NOT BREAK LARGE AMOUNTS OF CORRECT BEHAVIOR!
188: PLEASE ADD A TEST TO DEMONSTRATE YOUR CHANGES FIX SOMETHING.
189: The rollback exceptions are tested by org.jboss.test.jca.test.XAExceptionUnitTestCase
190: * @param e - the exception thrown by the invocation
191: * @param invocation
192: * @return the correct exception to throw
193: */
194: private Exception handleException(Throwable e, Invocation invocation) {
195:
196: InvocationType type = invocation.getType();
197: boolean isLocal = type == InvocationType.LOCAL
198: || type == InvocationType.LOCALHOME;
199:
200: if (e instanceof TransactionRolledbackLocalException
201: || e instanceof TransactionRolledbackException) {
202: // If we got a remote TransactionRolledbackException for a local
203: // invocation convert it into a TransactionRolledbackLocalException
204: if (isLocal && e instanceof TransactionRolledbackException) {
205: TransactionRolledbackException remoteTxRollback = (TransactionRolledbackException) e;
206:
207: Exception cause;
208: if (remoteTxRollback.detail instanceof Exception) {
209: cause = (Exception) remoteTxRollback.detail;
210: } else if (remoteTxRollback.detail instanceof Error) {
211: String msg = formatException("Unexpected Error",
212: remoteTxRollback.detail);
213: cause = new EJBException(msg);
214: } else {
215: String msg = formatException(
216: "Unexpected Throwable",
217: remoteTxRollback.detail);
218: cause = new EJBException(msg);
219: }
220:
221: e = new JBossTransactionRolledbackLocalException(
222: remoteTxRollback.getMessage(), cause);
223: }
224:
225: // If we got a local TransactionRolledbackLocalException for a remote
226: // invocation convert it into a TransactionRolledbackException
227: if (!isLocal
228: && e instanceof TransactionRolledbackLocalException) {
229: TransactionRolledbackLocalException localTxRollback = (TransactionRolledbackLocalException) e;
230: e = new JBossTransactionRolledbackException(
231: localTxRollback.getMessage(), localTxRollback
232: .getCausedByException());
233: }
234:
235: // get the data we need for logging
236: Throwable cause = null;
237: String exceptionType = null;
238: if (e instanceof TransactionRolledbackException) {
239: cause = ((TransactionRolledbackException) e).detail;
240: exceptionType = "TransactionRolledbackException";
241: } else {
242: cause = ((TransactionRolledbackLocalException) e)
243: .getCausedByException();
244: exceptionType = "TransactionRolledbackLocalException";
245: }
246:
247: // log the exception
248: if (cause != null) {
249: // if the cause is an EJBException unwrap it for logging
250: if ((cause instanceof EJBException)
251: && (((EJBException) cause)
252: .getCausedByException() != null)) {
253: cause = ((EJBException) cause)
254: .getCausedByException();
255: }
256: log
257: .error(exceptionType + " in method: "
258: + invocation.getMethod()
259: + ", causedBy:", cause);
260: } else {
261: log.error(exceptionType + " in method: "
262: + invocation.getMethod(), e);
263: }
264: return (Exception) e;
265: }
266: if (e instanceof NoSuchEntityException) {
267: NoSuchEntityException noSuchEntityException = (NoSuchEntityException) e;
268: if (noSuchEntityException.getCausedByException() != null) {
269: log.error("NoSuchEntityException in method: "
270: + invocation.getMethod() + ", causedBy:",
271: noSuchEntityException.getCausedByException());
272: } else {
273: log.error("NoSuchEntityException in method: "
274: + invocation.getMethod() + ":",
275: noSuchEntityException);
276: }
277:
278: if (isLocal) {
279: return new NoSuchObjectLocalException(
280: noSuchEntityException.getMessage(),
281: noSuchEntityException.getCausedByException());
282: } else {
283: NoSuchObjectException noSuchObjectException = new NoSuchObjectException(
284: noSuchEntityException.getMessage());
285: noSuchObjectException.detail = noSuchEntityException;
286: return noSuchObjectException;
287: }
288: }
289: if (e instanceof EJBException) {
290: EJBException ejbException = (EJBException) e;
291: if (ejbException.getCausedByException() != null) {
292: log.error("EJBException in method: "
293: + invocation.getMethod() + ", causedBy:",
294: ejbException.getCausedByException());
295: } else {
296: log.error("EJBException in method: "
297: + invocation.getMethod() + ":", ejbException);
298: }
299:
300: if (isLocal) {
301: return ejbException;
302: } else {
303: // Remote invocation need a remote exception
304: return new ServerException("EJBException:",
305: ejbException);
306: }
307: }
308:
309: /* Handle SecurityExceptions specially to tranform into one of the
310: security related ejb or rmi exceptions to allow users to identitify
311: them more easily.
312: */
313: if (e instanceof SecurityException
314: || e instanceof GeneralSecurityException) {
315: Exception runtimeException = (Exception) e;
316: if (log.isTraceEnabled())
317: log.trace("SecurityException in method: "
318: + invocation.getMethod() + ":",
319: runtimeException);
320: if (isAppException(invocation, e)) {
321: return runtimeException;
322: } else if (isLocal) {
323: return new AccessLocalException("SecurityException",
324: runtimeException);
325: } else {
326: return new AccessException("SecurityException",
327: runtimeException);
328: }
329: }
330:
331: // handle unmarshalling exception which should only come if problem unmarshalling
332: // invocation payload, arguments, or value on remote end.
333: if (e instanceof JBossLazyUnmarshallingException) {
334: RuntimeException runtimeException = (RuntimeException) e;
335: log.error("UnmarshalException:", e);
336:
337: if (isLocal) {
338: return new EJBException("UnmarshalException",
339: runtimeException);
340: } else {
341: return new MarshalException("MarshalException",
342: runtimeException);
343: }
344: }
345:
346: // All other RuntimeException
347: if (e instanceof RuntimeException) {
348: RuntimeException runtimeException = (RuntimeException) e;
349: log.error("RuntimeException in method: "
350: + invocation.getMethod() + ":", runtimeException);
351:
352: if (isLocal) {
353: return new EJBException("RuntimeException",
354: runtimeException);
355: } else {
356: return new ServerException("RuntimeException",
357: runtimeException);
358: }
359: }
360: if (e instanceof Error) {
361: log.error("Unexpected Error in method: "
362: + invocation.getMethod(), e);
363: if (isLocal) {
364: String msg = formatException("Unexpected Error", e);
365: return new EJBException(msg);
366: } else {
367: return new ServerError("Unexpected Error", (Error) e);
368: }
369: }
370:
371: // If we got a RemoteException for a local invocation wrap it
372: // in an EJBException.
373: if (isLocal && e instanceof RemoteException) {
374: if (callLogging) {
375: log.info("Remote Exception in method: "
376: + invocation.getMethod(), e);
377: }
378: return new EJBException((RemoteException) e);
379: }
380:
381: if (e instanceof Exception) {
382: if (callLogging) {
383: log.info("Application Exception in method: "
384: + invocation.getMethod(), e);
385: }
386: return (Exception) e;
387: } else {
388: // The should not happen
389: String msg = formatException("Unexpected Throwable", e);
390: log.warn("Unexpected Throwable in method: "
391: + invocation.getMethod(), e);
392: if (isLocal) {
393: return new EJBException(msg);
394: } else {
395: return new ServerException(msg);
396: }
397: }
398: }
399:
400: private String formatException(String msg, Throwable t) {
401: StringWriter sw = new StringWriter();
402: PrintWriter pw = new PrintWriter(sw);
403: if (msg != null)
404: pw.println(msg);
405: if (t != null) {
406: t.printStackTrace(pw);
407: } // end of if ()
408: return sw.toString();
409: }
410:
411: }
|