/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb.plugins;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.rmi.*;
import java.security.GeneralSecurityException;
import javax.ejb.EJBException;
import javax.ejb.NoSuchEntityException;
import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.TransactionRolledbackLocalException;
import javax.ejb.AccessLocalException;
import javax.transaction.TransactionRolledbackException;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.InvocationType;
import org.jboss.invocation.JBossLazyUnmarshallingException;
import org.jboss.metadata.BeanMetaData;
import org.jboss.tm.JBossTransactionRolledbackException;
import org.jboss.tm.JBossTransactionRolledbackLocalException;
/**
* An interceptor used to log all invocations. It also handles any
* unexpected exceptions.
*
* @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
* @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
* @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
* @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
* @version $Revision: 81030 $
*/
public class LogInterceptor extends AbstractInterceptor
{
// Static --------------------------------------------------------
// Attributes ----------------------------------------------------
protected String ejbName;
protected boolean callLogging;
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
// Container implementation --------------------------------------
public void create()
throws Exception
{
super.start();
BeanMetaData md = getContainer().getBeanMetaData();
ejbName = md.getEjbName();
// Should we log call details
callLogging = md.getContainerConfiguration().getCallLogging();
}
/**
* This method logs the method, calls the next invoker, and handles
* any exception.
*
* @param invocation contain all infomation necessary to carry out the
* invocation
* @return the return value of the invocation
* @exception Exception if an exception during the invocation
*/
public Object invokeHome(Invocation invocation)
throws Exception
{
String methodName;
if (invocation.getMethod() != null)
{
methodName = invocation.getMethod().getName();
}
else
{
methodName = "<no method>";
}
boolean trace = log.isTraceEnabled();
if (trace)
{
log.trace("Start method=" + methodName);
}
// Log call details
if (callLogging)
{
StringBuffer str = new StringBuffer("InvokeHome: ");
str.append(methodName);
str.append("(");
Object[] args = invocation.getArguments();
if (args != null)
{
for (int i = 0; i < args.length; i++)
{
if (i > 0)
{
str.append(",");
}
str.append(args[i]);
}
}
str.append(")");
log.debug(str.toString());
}
try
{
return getNext().invokeHome(invocation);
}
catch(Throwable e)
{
throw handleException(e, invocation);
}
finally
{
if (trace)
{
log.trace("End method=" + methodName);
}
}
}
/**
* This method logs the method, calls the next invoker, and handles
* any exception.
*
* @param invocation contain all infomation necessary to carry out the
* invocation
* @return the return value of the invocation
* @exception Exception if an exception during the invocation
*/
public Object invoke(Invocation invocation)
throws Exception
{
String methodName;
if (invocation.getMethod() != null)
{
methodName = invocation.getMethod().getName();
}
else
{
methodName = "<no method>";
}
boolean trace = log.isTraceEnabled();
if (trace)
{
log.trace("Start method=" + methodName);
}
// Log call details
if (callLogging)
{
StringBuffer str = new StringBuffer("Invoke: ");
if (invocation.getId() != null)
{
str.append("[");
str.append(invocation.getId().toString());
str.append("] ");
}
str.append(methodName);
str.append("(");
Object[] args = invocation.getArguments();
if (args != null)
{
for (int i = 0; i < args.length; i++)
{
if (i > 0)
{
str.append(",");
}
str.append(args[i]);
}
}
str.append(")");
log.debug(str.toString());
}
try
{
return getNext().invoke(invocation);
}
catch(Throwable e)
{
throw handleException(e, invocation);
}
finally
{
if (trace)
{
log.trace("End method=" + methodName);
}
}
}
// Private -------------------------------------------------------
/**
PLEASE DO NOT CHANGE THIS CODE WITHOUT LOOKING AT __ALL__ OF IT TO MAKE ___SURE___
YOUR CHANGES ARE NECESSARY AND DO NOT BREAK LARGE AMOUNTS OF CORRECT BEHAVIOR!
PLEASE ADD A TEST TO DEMONSTRATE YOUR CHANGES FIX SOMETHING.
The rollback exceptions are tested by org.jboss.test.jca.test.XAExceptionUnitTestCase
* @param e - the exception thrown by the invocation
* @param invocation
* @return the correct exception to throw
*/
private Exception handleException(Throwable e, Invocation invocation)
{
InvocationType type = invocation.getType();
boolean isLocal =
type == InvocationType.LOCAL ||
type == InvocationType.LOCALHOME;
if (e instanceof TransactionRolledbackLocalException ||
e instanceof TransactionRolledbackException)
{
// If we got a remote TransactionRolledbackException for a local
// invocation convert it into a TransactionRolledbackLocalException
if (isLocal && e instanceof TransactionRolledbackException)
{
TransactionRolledbackException remoteTxRollback =
(TransactionRolledbackException)e;
Exception cause;
if (remoteTxRollback.detail instanceof Exception)
{
cause = (Exception)remoteTxRollback.detail;
}
else if (remoteTxRollback.detail instanceof Error)
{
String msg = formatException(
"Unexpected Error",
remoteTxRollback.detail);
cause = new EJBException(msg);
}
else
{
String msg = formatException(
"Unexpected Throwable",
remoteTxRollback.detail);
cause = new EJBException(msg);
}
e = new JBossTransactionRolledbackLocalException(
remoteTxRollback.getMessage(),
cause);
}
// If we got a local TransactionRolledbackLocalException for a remote
// invocation convert it into a TransactionRolledbackException
if (!isLocal && e instanceof TransactionRolledbackLocalException)
{
TransactionRolledbackLocalException localTxRollback =
(TransactionRolledbackLocalException)e;
e = new JBossTransactionRolledbackException(
localTxRollback.getMessage(), localTxRollback.getCausedByException());
}
// get the data we need for logging
Throwable cause = null;
String exceptionType = null;
if (e instanceof TransactionRolledbackException)
{
cause = ((TransactionRolledbackException)e).detail;
exceptionType = "TransactionRolledbackException";
}
else
{
cause =
((TransactionRolledbackLocalException)e).getCausedByException();
exceptionType = "TransactionRolledbackLocalException";
}
// log the exception
if (cause != null)
{
// if the cause is an EJBException unwrap it for logging
if ((cause instanceof EJBException) &&
(((EJBException) cause).getCausedByException() != null))
{
cause = ((EJBException) cause).getCausedByException();
}
log.error(exceptionType + " in method: " + invocation.getMethod()
+ ", causedBy:", cause);
}
else
{
log.error(exceptionType + " in method: " + invocation.getMethod(), e);
}
return (Exception)e;
}
if (e instanceof NoSuchEntityException)
{
NoSuchEntityException noSuchEntityException =
(NoSuchEntityException) e;
if (noSuchEntityException.getCausedByException() != null)
{
log.error("NoSuchEntityException in method: " + invocation.getMethod() + ", causedBy:",
noSuchEntityException.getCausedByException());
}
else
{
log.error("NoSuchEntityException in method: " + invocation.getMethod() + ":", noSuchEntityException);
}
if (isLocal)
{
return new NoSuchObjectLocalException(
noSuchEntityException.getMessage(),
noSuchEntityException.getCausedByException());
}
else
{
NoSuchObjectException noSuchObjectException =
new NoSuchObjectException(noSuchEntityException.getMessage());
noSuchObjectException.detail = noSuchEntityException;
return noSuchObjectException;
}
}
if (e instanceof EJBException)
{
EJBException ejbException = (EJBException) e;
if (ejbException.getCausedByException() != null)
{
log.error("EJBException in method: " + invocation.getMethod() + ", causedBy:",
ejbException.getCausedByException());
}
else
{
log.error("EJBException in method: " + invocation.getMethod() + ":", ejbException);
}
if (isLocal)
{
return ejbException;
}
else
{
// Remote invocation need a remote exception
return new ServerException("EJBException:", ejbException);
}
}
/* Handle SecurityExceptions specially to tranform into one of the
security related ejb or rmi exceptions to allow users to identitify
them more easily.
*/
if (e instanceof SecurityException || e instanceof GeneralSecurityException)
{
Exception runtimeException = (Exception)e;
if( log.isTraceEnabled() )
log.trace("SecurityException in method: " + invocation.getMethod() + ":", runtimeException);
if( isAppException(invocation, e) )
{
return runtimeException;
}
else if (isLocal)
{
return new AccessLocalException("SecurityException", runtimeException);
}
else
{
return new AccessException("SecurityException", runtimeException);
}
}
// handle unmarshalling exception which should only come if problem unmarshalling
// invocation payload, arguments, or value on remote end.
if(e instanceof JBossLazyUnmarshallingException)
{
RuntimeException runtimeException = (RuntimeException)e;
log.error("UnmarshalException:", e);
if(isLocal)
{
return new EJBException("UnmarshalException", runtimeException);
}
else
{
return new MarshalException("MarshalException", runtimeException);
}
}
// All other RuntimeException
if (e instanceof RuntimeException)
{
RuntimeException runtimeException = (RuntimeException)e;
log.error("RuntimeException in method: " + invocation.getMethod() + ":", runtimeException);
if (isLocal)
{
return new EJBException("RuntimeException", runtimeException);
}
else
{
return new ServerException("RuntimeException", runtimeException);
}
}
if (e instanceof Error)
{
log.error("Unexpected Error in method: " + invocation.getMethod(), e);
if (isLocal)
{
String msg = formatException("Unexpected Error", e);
return new EJBException(msg);
}
else
{
return new ServerError("Unexpected Error", (Error)e);
}
}
// If we got a RemoteException for a local invocation wrap it
// in an EJBException.
if(isLocal && e instanceof RemoteException)
{
if (callLogging)
{
log.info("Remote Exception in method: " + invocation.getMethod(), e);
}
return new EJBException((RemoteException)e);
}
if (e instanceof Exception)
{
if (callLogging)
{
log.info("Application Exception in method: " + invocation.getMethod(), e);
}
return (Exception)e;
}
else
{
// The should not happen
String msg = formatException("Unexpected Throwable", e);
log.warn("Unexpected Throwable in method: " + invocation.getMethod(), e);
if (isLocal)
{
return new EJBException(msg);
}
else
{
return new ServerException(msg);
}
}
}
private String formatException(String msg, Throwable t)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
if (msg != null)
pw.println(msg);
if (t != null)
{
t.printStackTrace(pw);
} // end of if ()
return sw.toString();
}
}