1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.ejb.plugins;
23
24 import java.io.PrintWriter;
25 import java.io.StringWriter;
26 import java.rmi;
27 import java.security.GeneralSecurityException;
28
29 import javax.ejb.EJBException;
30 import javax.ejb.NoSuchEntityException;
31 import javax.ejb.NoSuchObjectLocalException;
32 import javax.ejb.TransactionRolledbackLocalException;
33 import javax.ejb.AccessLocalException;
34 import javax.transaction.TransactionRolledbackException;
35
36
37 import org.jboss.invocation.Invocation;
38 import org.jboss.invocation.InvocationType;
39 import org.jboss.invocation.JBossLazyUnmarshallingException;
40 import org.jboss.metadata.BeanMetaData;
41
42 import org.jboss.tm.JBossTransactionRolledbackException;
43 import org.jboss.tm.JBossTransactionRolledbackLocalException;
44
45 /**
46 * An interceptor used to log all invocations. It also handles any
47 * unexpected exceptions.
48 *
49 * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
50 * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
51 * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
52 * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
53 * @version $Revision: 66439 $
54 */
55 public class LogInterceptor extends AbstractInterceptor
56 {
57 // Static --------------------------------------------------------
58
59 // Attributes ----------------------------------------------------
60 protected String ejbName;
61 protected boolean callLogging;
62
63 // Constructors --------------------------------------------------
64
65 // Public --------------------------------------------------------
66
67 // Container implementation --------------------------------------
68 public void create()
69 throws Exception
70 {
71 super.start();
72
73 BeanMetaData md = getContainer().getBeanMetaData();
74 ejbName = md.getEjbName();
75
76 // Should we log call details
77 callLogging = md.getContainerConfiguration().getCallLogging();
78 }
79
80 /**
81 * This method logs the method, calls the next invoker, and handles
82 * any exception.
83 *
84 * @param invocation contain all infomation necessary to carry out the
85 * invocation
86 * @return the return value of the invocation
87 * @exception Exception if an exception during the invocation
88 */
89 public Object invokeHome(Invocation invocation)
90 throws Exception
91 {
92 String methodName;
93 if (invocation.getMethod() != null)
94 {
95 methodName = invocation.getMethod().getName();
96 }
97 else
98 {
99 methodName = "<no method>";
100 }
101
102 boolean trace = log.isTraceEnabled();
103 if (trace)
104 {
105 log.trace("Start method=" + methodName);
106 }
107
108 // Log call details
109 if (callLogging)
110 {
111 StringBuffer str = new StringBuffer("InvokeHome: ");
112 str.append(methodName);
113 str.append("(");
114 Object[] args = invocation.getArguments();
115 if (args != null)
116 {
117 for (int i = 0; i < args.length; i++)
118 {
119 if (i > 0)
120 {
121 str.append(",");
122 }
123 str.append(args[i]);
124 }
125 }
126 str.append(")");
127 log.debug(str.toString());
128 }
129
130 try
131 {
132 return getNext().invokeHome(invocation);
133 }
134 catch(Throwable e)
135 {
136 throw handleException(e, invocation);
137 }
138 finally
139 {
140 if (trace)
141 {
142 log.trace("End method=" + methodName);
143 }
144 }
145 }
146
147 /**
148 * This method logs the method, calls the next invoker, and handles
149 * any exception.
150 *
151 * @param invocation contain all infomation necessary to carry out the
152 * invocation
153 * @return the return value of the invocation
154 * @exception Exception if an exception during the invocation
155 */
156 public Object invoke(Invocation invocation)
157 throws Exception
158 {
159 String methodName;
160 if (invocation.getMethod() != null)
161 {
162 methodName = invocation.getMethod().getName();
163 }
164 else
165 {
166 methodName = "<no method>";
167 }
168
169 boolean trace = log.isTraceEnabled();
170 if (trace)
171 {
172 log.trace("Start method=" + methodName);
173 }
174
175 // Log call details
176 if (callLogging)
177 {
178 StringBuffer str = new StringBuffer("Invoke: ");
179 if (invocation.getId() != null)
180 {
181 str.append("[");
182 str.append(invocation.getId().toString());
183 str.append("] ");
184 }
185 str.append(methodName);
186 str.append("(");
187 Object[] args = invocation.getArguments();
188 if (args != null)
189 {
190 for (int i = 0; i < args.length; i++)
191 {
192 if (i > 0)
193 {
194 str.append(",");
195 }
196 str.append(args[i]);
197 }
198 }
199 str.append(")");
200 log.debug(str.toString());
201 }
202
203 try
204 {
205 return getNext().invoke(invocation);
206 }
207 catch(Throwable e)
208 {
209 throw handleException(e, invocation);
210 }
211 finally
212 {
213 if (trace)
214 {
215 log.trace("End method=" + methodName);
216 }
217 }
218 }
219
220 // Private -------------------------------------------------------
221
222 /**
223 PLEASE DO NOT CHANGE THIS CODE WITHOUT LOOKING AT __ALL__ OF IT TO MAKE ___SURE___
224 YOUR CHANGES ARE NECESSARY AND DO NOT BREAK LARGE AMOUNTS OF CORRECT BEHAVIOR!
225 PLEASE ADD A TEST TO DEMONSTRATE YOUR CHANGES FIX SOMETHING.
226 The rollback exceptions are tested by org.jboss.test.jca.test.XAExceptionUnitTestCase
227 * @param e - the exception thrown by the invocation
228 * @param invocation
229 * @return the correct exception to throw
230 */
231 private Exception handleException(Throwable e, Invocation invocation)
232 {
233
234 InvocationType type = invocation.getType();
235 boolean isLocal =
236 type == InvocationType.LOCAL ||
237 type == InvocationType.LOCALHOME;
238
239 if (e instanceof TransactionRolledbackLocalException ||
240 e instanceof TransactionRolledbackException)
241 {
242 // If we got a remote TransactionRolledbackException for a local
243 // invocation convert it into a TransactionRolledbackLocalException
244 if (isLocal && e instanceof TransactionRolledbackException)
245 {
246 TransactionRolledbackException remoteTxRollback =
247 (TransactionRolledbackException)e;
248
249 Exception cause;
250 if (remoteTxRollback.detail instanceof Exception)
251 {
252 cause = (Exception)remoteTxRollback.detail;
253 }
254 else if (remoteTxRollback.detail instanceof Error)
255 {
256 String msg = formatException(
257 "Unexpected Error",
258 remoteTxRollback.detail);
259 cause = new EJBException(msg);
260 }
261 else
262 {
263 String msg = formatException(
264 "Unexpected Throwable",
265 remoteTxRollback.detail);
266 cause = new EJBException(msg);
267 }
268
269 e = new JBossTransactionRolledbackLocalException(
270 remoteTxRollback.getMessage(),
271 cause);
272 }
273
274 // If we got a local TransactionRolledbackLocalException for a remote
275 // invocation convert it into a TransactionRolledbackException
276 if (!isLocal && e instanceof TransactionRolledbackLocalException)
277 {
278 TransactionRolledbackLocalException localTxRollback =
279 (TransactionRolledbackLocalException)e;
280 e = new JBossTransactionRolledbackException(
281 localTxRollback.getMessage(), localTxRollback.getCausedByException());
282 }
283
284 // get the data we need for logging
285 Throwable cause = null;
286 String exceptionType = null;
287 if (e instanceof TransactionRolledbackException)
288 {
289 cause = ((TransactionRolledbackException)e).detail;
290 exceptionType = "TransactionRolledbackException";
291 }
292 else
293 {
294 cause =
295 ((TransactionRolledbackLocalException)e).getCausedByException();
296 exceptionType = "TransactionRolledbackLocalException";
297 }
298
299 // log the exception
300 if (cause != null)
301 {
302 // if the cause is an EJBException unwrap it for logging
303 if ((cause instanceof EJBException) &&
304 (((EJBException) cause).getCausedByException() != null))
305 {
306 cause = ((EJBException) cause).getCausedByException();
307 }
308 log.error(exceptionType + " in method: " + invocation.getMethod()
309 + ", causedBy:", cause);
310 }
311 else
312 {
313 log.error(exceptionType + " in method: " + invocation.getMethod(), e);
314 }
315 return (Exception)e;
316 }
317 if (e instanceof NoSuchEntityException)
318 {
319 NoSuchEntityException noSuchEntityException =
320 (NoSuchEntityException) e;
321 if (noSuchEntityException.getCausedByException() != null)
322 {
323 log.error("NoSuchEntityException in method: " + invocation.getMethod() + ", causedBy:",
324 noSuchEntityException.getCausedByException());
325 }
326 else
327 {
328 log.error("NoSuchEntityException in method: " + invocation.getMethod() + ":", noSuchEntityException);
329 }
330
331 if (isLocal)
332 {
333 return new NoSuchObjectLocalException(
334 noSuchEntityException.getMessage(),
335 noSuchEntityException.getCausedByException());
336 }
337 else
338 {
339 NoSuchObjectException noSuchObjectException =
340 new NoSuchObjectException(noSuchEntityException.getMessage());
341 noSuchObjectException.detail = noSuchEntityException;
342 return noSuchObjectException;
343 }
344 }
345 if (e instanceof EJBException)
346 {
347 EJBException ejbException = (EJBException) e;
348 if (ejbException.getCausedByException() != null)
349 {
350 log.error("EJBException in method: " + invocation.getMethod() + ", causedBy:",
351 ejbException.getCausedByException());
352 }
353 else
354 {
355 log.error("EJBException in method: " + invocation.getMethod() + ":", ejbException);
356 }
357
358 if (isLocal)
359 {
360 return ejbException;
361 }
362 else
363 {
364 // Remote invocation need a remote exception
365 return new ServerException("EJBException:", ejbException);
366 }
367 }
368
369 /* Handle SecurityExceptions specially to tranform into one of the
370 security related ejb or rmi exceptions to allow users to identitify
371 them more easily.
372 */
373 if (e instanceof SecurityException || e instanceof GeneralSecurityException)
374 {
375 Exception runtimeException = (Exception)e;
376 if( log.isTraceEnabled() )
377 log.trace("SecurityException in method: " + invocation.getMethod() + ":", runtimeException);
378 if( isAppException(invocation, e) )
379 {
380 return runtimeException;
381 }
382 else if (isLocal)
383 {
384 return new AccessLocalException("SecurityException", runtimeException);
385 }
386 else
387 {
388 return new AccessException("SecurityException", runtimeException);
389 }
390 }
391
392 // handle unmarshalling exception which should only come if problem unmarshalling
393 // invocation payload, arguments, or value on remote end.
394 if(e instanceof JBossLazyUnmarshallingException)
395 {
396 RuntimeException runtimeException = (RuntimeException)e;
397 log.error("UnmarshalException:", e);
398
399 if(isLocal)
400 {
401 return new EJBException("UnmarshalException", runtimeException);
402 }
403 else
404 {
405 return new MarshalException("MarshalException", runtimeException);
406 }
407 }
408
409 // All other RuntimeException
410 if (e instanceof RuntimeException)
411 {
412 RuntimeException runtimeException = (RuntimeException)e;
413 log.error("RuntimeException in method: " + invocation.getMethod() + ":", runtimeException);
414
415 if (isLocal)
416 {
417 return new EJBException("RuntimeException", runtimeException);
418 }
419 else
420 {
421 return new ServerException("RuntimeException", runtimeException);
422 }
423 }
424 if (e instanceof Error)
425 {
426 log.error("Unexpected Error in method: " + invocation.getMethod(), e);
427 if (isLocal)
428 {
429 String msg = formatException("Unexpected Error", e);
430 return new EJBException(msg);
431 }
432 else
433 {
434 return new ServerError("Unexpected Error", (Error)e);
435 }
436 }
437
438 // If we got a RemoteException for a local invocation wrap it
439 // in an EJBException.
440 if(isLocal && e instanceof RemoteException)
441 {
442 if (callLogging)
443 {
444 log.info("Remote Exception in method: " + invocation.getMethod(), e);
445 }
446 return new EJBException((RemoteException)e);
447 }
448
449 if (e instanceof Exception)
450 {
451 if (callLogging)
452 {
453 log.info("Application Exception in method: " + invocation.getMethod(), e);
454 }
455 return (Exception)e;
456 }
457 else
458 {
459 // The should not happen
460 String msg = formatException("Unexpected Throwable", e);
461 log.warn("Unexpected Throwable in method: " + invocation.getMethod(), e);
462 if (isLocal)
463 {
464 return new EJBException(msg);
465 }
466 else
467 {
468 return new ServerException(msg);
469 }
470 }
471 }
472
473 private String formatException(String msg, Throwable t)
474 {
475 StringWriter sw = new StringWriter();
476 PrintWriter pw = new PrintWriter(sw);
477 if (msg != null)
478 pw.println(msg);
479 if (t != null)
480 {
481 t.printStackTrace(pw);
482 } // end of if ()
483 return sw.toString();
484 }
485
486 }