package org.dcache.srm.util; import org.apache.axis.AxisFault; import org.apache.axis.MessageContext; import org.apache.axis.providers.java.RPCProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.rmi.RemoteException; /** * This class wraps the default Axis Java dispatcher (RPCProvider) to provide * reasonable logging of problems via slf4j. * * Apache Axis allows for custom dispatch through a class that implements * the org.apache.axis.Handler interface. The standard handler, RPCProvider, * invokes a method on the user-supplied class with the same name as the RPC * call. RPCProvider uses a method invokeMethod that contains a single call to * the method, via reflection. This allows superclasses to decorate how the * method call happens; for example, by adding additional security checks. * * Unfortunately, the error reporting in Axis is less than ideal. Exceptions * that do not represent SOAP Faults are not logged on the server-side, but are * reported back to the client as a SOAP Fault. * * This class fixes the problem by wrapping the RPC invocation and logging * activity and problems. It follows the anti-pattern of catch-log-throw, * which risks logging the same problem twice; however, this seems to be * required in order to guarantee that problems are reported in a meaningful * way. */ public class LoggingRPCProvider extends RPCProvider { private static final long serialVersionUID = 1L; private static final Logger _log = LoggerFactory.getLogger(LoggingRPCProvider.class); @Override protected Object invokeMethod(MessageContext msgContext, Method method, Object obj, Object[] args) throws Exception { String className = obj.getClass().getCanonicalName(); String methodName = method.getName(); _log.trace("Invoking {}#{} with {}", className, methodName, args); try { Object result = super.invokeMethod(msgContext, method, obj, args); if (method.getReturnType().equals(Void.TYPE)) { _log.trace("Invocation of {}#{} completed", className, methodName); } else { if (result == null) { _log.trace("Invocation of {}#{} returned null", className, methodName); } else { _log.trace("Invocation of {}#{} returned {}: {}", className, methodName, result.getClass().getCanonicalName(), result); } } return result; } catch(InvocationTargetException e) { Throwable t = e.getCause(); // Axis wraps RuntimeException (i.e., bugs) in an AxisFault without // logging them. We unwrap them here so we can log them. if (t instanceof AxisFault && t.getCause() instanceof RuntimeException) { t = t.getCause(); } if (t instanceof AxisFault) { AxisFault fault = (AxisFault) t; /* * All exceptions that are to be delivered as a SOAP Fault are * subclasses of AxisFault. */ _log.trace("Invocation produced AxisFault {}: code={}, " + "reason={}, string={}", fault.getClass().getSimpleName(), fault.getFaultCode(), fault.getFaultReason(), fault.getFaultString()); } else if(t instanceof RemoteException) { if (t.getCause() == null) { _log.debug("Invocation produced fault: {}", t.getMessage()); } else { _log.debug("Invocation produced fault: {} (caused by {})", t.getMessage(), t.getCause()); } } else if(t instanceof RuntimeException) { _log.error("Bug detected, please report this to " + "support@dCache.org", t); } else { _log.error("Unexpected invocation exception", t); } throw e; } catch(RuntimeException e) { _log.error("Bug detected, please report this to " + "support@dCache.org", e); throw e; } } }