Author: barrettj Date: Sun Feb 27 14:11:00 2011 New Revision: 1075057 URL: http://svn.apache.org/viewvc?rev=1075057&view=rev Log: Prevent index out of bounds exception, or any exception, from attempting to log. Add TDD/UT for same.
Added: axis/axis2/java/core/trunk/modules/jaxws/test/org/apache/axis2/jaxws/WebServiceExceptionLoggerTests.java Modified: axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/WebServiceExceptionLogger.java Modified: axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/WebServiceExceptionLogger.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/WebServiceExceptionLogger.java?rev=1075057&r1=1075056&r2=1075057&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/WebServiceExceptionLogger.java (original) +++ axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/WebServiceExceptionLogger.java Sun Feb 27 14:11:00 2011 @@ -52,68 +52,80 @@ public class WebServiceExceptionLogger { Class serviceImplClass, Object serviceInstance, Object[] args) { - - // Must have debug or error logging enabled - if (!log.isDebugEnabled() && !log.isErrorEnabled()) { - return; - } - - // Get the root of the exception - Throwable rootT = null; - if (throwable instanceof InvocationTargetException) { - rootT = ((InvocationTargetException) throwable).getTargetException(); - } - - - String name = rootT.getClass().getName(); - String stack = stackToString(rootT); - - // Determine if this is a checked exception or non-checked exception - Class checkedException = JavaUtils.getCheckedException(rootT, method); - - if (checkedException == null) { - // Only log errors for non-checked exceptions - if (log.isErrorEnabled()) { - String text = ""; - if (logFully) { - text = Messages.getMessage("failureLogger", name, rootT.toString()); - - } else { - text = Messages.getMessage("failureLogger", name, stack); - } - log.error(text); + + // No matter what happens in this logging method, do not surface that exception. We don't want a logging + // failure to mask the real exception we are trying to log or affect subsequent processing. + try { + + // Must have debug or error logging enabled + if (!log.isDebugEnabled() && !log.isErrorEnabled()) { + return; } - } - - // Full logging if debug is enabled. - if (log.isDebugEnabled()) { - log.debug("Exception invoking a method of " + serviceImplClass.toString() - + " of instance " + serviceInstance.toString()); - log.debug("Exception type thrown: " + throwable.getClass().getName()); - if (rootT != null) { - log.debug("Root Exception type thrown: " + rootT.getClass().getName()); - } - if (checkedException != null) { - log.debug("The exception is an instance of checked exception: " + - checkedException.getName()); + // Get the root of the exception + Throwable rootT = null; + if (throwable instanceof InvocationTargetException) { + rootT = ((InvocationTargetException) throwable).getTargetException(); + } else { + rootT = throwable; } - // Extra trace if ElementNSImpl incompatibility problem. - // The incompatibility exception occurs if the JAXB Unmarshaller - // unmarshals to a dom element instead of a generated object. This can - // result in class cast exceptions. The solution is usually a missing - // @XmlSeeAlso annotation in the jaxws or jaxb classes. - if (rootT.toString().contains("org.apache.xerces.dom.ElementNSImpl incompatible")) { - log.debug("This exception may be due to a missing @XmlSeeAlso in the client's jaxws or" + - " jaxb classes."); + String name = rootT.getClass().getName(); + log.debug("693210: root Throwable, may cause index error: " + rootT.toString(), rootT); + String stack = stackToString(rootT); + + // Determine if this is a checked exception or non-checked exception + Class checkedException = JavaUtils.getCheckedException(rootT, method); + + if (checkedException == null) { + // Only log errors for non-checked exceptions + if (log.isErrorEnabled()) { + String text = ""; + if (logFully) { + text = Messages.getMessage("failureLogger", name, rootT.toString()); + + } else { + text = Messages.getMessage("failureLogger", name, stack); + } + log.error(text); + } + + } + + // Full logging if debug is enabled. + if (log.isDebugEnabled()) { + log.debug("Exception invoking a method of " + serviceImplClass.toString() + + " of instance " + serviceInstance.toString()); + log.debug("Exception type thrown: " + throwable.getClass().getName()); + if (rootT != null) { + log.debug("Root Exception type thrown: " + rootT.getClass().getName()); + } + if (checkedException != null) { + log.debug("The exception is an instance of checked exception: " + + checkedException.getName()); + } + + // Extra trace if ElementNSImpl incompatibility problem. + // The incompatibility exception occurs if the JAXB Unmarshaller + // unmarshals to a dom element instead of a generated object. This can + // result in class cast exceptions. The solution is usually a missing + // @XmlSeeAlso annotation in the jaxws or jaxb classes. + if (rootT.toString().contains("org.apache.xerces.dom.ElementNSImpl incompatible")) { + log.debug("This exception may be due to a missing @XmlSeeAlso in the client's jaxws or" + + " jaxb classes."); + } + log.debug("Method = " + method.toGenericString()); + for (int i = 0; i < args.length; i++) { + String value = + (args[i] == null) ? "null" + : args[i].getClass().toString(); + log.debug(" Argument[" + i + "] is " + value); + } } - log.debug("Method = " + method.toGenericString()); - for (int i = 0; i < args.length; i++) { - String value = - (args[i] == null) ? "null" - : args[i].getClass().toString(); - log.debug(" Argument[" + i + "] is " + value); + } catch (Throwable t) { + if (log.isDebugEnabled()) { + log.debug("Caught throwable in logger", t); + log.debug("While attempting to log original exception", throwable); } } return; @@ -126,15 +138,18 @@ public class WebServiceExceptionLogger { * @param e * @return */ - private static String stackToString(Throwable e) { + static String stackToString(Throwable e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.BufferedWriter bw = new java.io.BufferedWriter(sw); java.io.PrintWriter pw = new java.io.PrintWriter(bw); e.printStackTrace(pw); pw.close(); String text = sw.getBuffer().toString(); - // Jump past the throwable - text = text.substring(text.indexOf("at")); + // Jump past the name of the throwable cause in the stack + int indexOfThrowable = text.indexOf("at"); + if (indexOfThrowable > 0) { + text = text.substring(indexOfThrowable); + } return text; } Added: axis/axis2/java/core/trunk/modules/jaxws/test/org/apache/axis2/jaxws/WebServiceExceptionLoggerTests.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws/test/org/apache/axis2/jaxws/WebServiceExceptionLoggerTests.java?rev=1075057&view=auto ============================================================================== --- axis/axis2/java/core/trunk/modules/jaxws/test/org/apache/axis2/jaxws/WebServiceExceptionLoggerTests.java (added) +++ axis/axis2/java/core/trunk/modules/jaxws/test/org/apache/axis2/jaxws/WebServiceExceptionLoggerTests.java Sun Feb 27 14:11:00 2011 @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.jaxws; + +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import junit.framework.TestCase; + +/** + * Validate the WebServiceExeptionLogger + */ +public class WebServiceExceptionLoggerTests extends TestCase { + + /** + * Validate that if we pass in an exception without any stack information, it does not cause an exception. + */ + public void testNoStack() { + + ServiceClass serviceClass = new ServiceClass(); + Method method = null; + try { + method = ImplClass.class.getDeclaredMethod("implMethod"); + } catch (Exception e) { + e.printStackTrace(); + fail("Test implementation error: unable to get test class method due to exception: " + e.toString()); + } + Exception rootCause = new EmptyStackTraceException(); + InvocationTargetException invTargetExc = new InvocationTargetException(rootCause); + WebServiceExceptionLogger.log(method, + invTargetExc, + false, + ImplClass.class, + serviceClass, + null); + } + + /** + * Validate that a exception that is not of type InvocationTargetException does not cause any issues in the logger. + */ + public void testNonInvocationTargetException() { + + ServiceClass serviceClass = new ServiceClass(); + Method method = null; + try { + method = ImplClass.class.getDeclaredMethod("implMethod"); + } catch (Exception e) { + e.printStackTrace(); + fail("Test implementation error: unable to get test class method due to exception: " + e.toString()); + } + Exception rootCause = new NonemptyStackTraceException(); + WebServiceExceptionLogger.log(method, + rootCause, + false, + ImplClass.class, + serviceClass, + null); + } + + /** + * Validate the stackToString method works if there is no stack returned by the exception passed in. It is necessary + * to validate this method directly since the WebServcieExceptionLogger.log method will catch all exceptions so it + * will never fail. + */ + public void testStackToStrig_NoStack() { + String returnedStack = WebServiceExceptionLogger.stackToString(new EmptyStackTraceException()); + // If we don't get an exception, then this test passes. + } + + /** + * Validate that the returned stack skips over the original throwable's name. + */ + public void testStackToString() { + Exception nonEmpty = new NonemptyStackTraceException(); + String returnedStack = WebServiceExceptionLogger.stackToString(nonEmpty); + assertFalse("Returned stack did not skip the original throwable", returnedStack.contains("NonemptyStackTraceException")); + } + + class ImplClass { + + public String implMethod() { return "foo"; } + + } + + class ServiceClass { + + } + + class EmptyStackTraceException extends Exception { + public void printStackTrace(PrintWriter err) { } + } + + class NonemptyStackTraceException extends Exception { + } +}