// Copyright � 2002-2007 Canoo Engineering AG, Switzerland. package com.canoo.webtest.steps; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.net.SocketTimeoutException; import javax.xml.xpath.XPathException; import org.apache.log4j.Logger; import org.apache.xerces.xni.XNIException; import org.xml.sax.SAXException; import com.canoo.webtest.engine.StepExecutionException; import com.canoo.webtest.engine.StepFailedException; import com.canoo.webtest.engine.WebTestException; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.ScriptException; import com.gargoylesoftware.htmlunit.html.HtmlPage; /** * Helper class for {@link Step}. * @author Carsten Seibert * @author Marc Guillemot * @author Paul King */ public class StepUtil { private static final Logger LOG = Logger.getLogger(StepUtil.class); static Throwable extractNestedException(final Throwable e) { Throwable originalException = e; Throwable cause = ((XNIException) e).getException(); while (cause != null) { originalException = cause; if (cause instanceof XNIException) { cause = ((XNIException) cause).getException(); } else if (cause instanceof InvocationTargetException) { cause = cause.getCause(); } else { cause = null; } } return originalException; } /** * Called if {@link Step#doExecute()} throws an exception * @param e the thrown exception */ public static void handleException(Throwable e) { if (e instanceof WebTestException) { throw (WebTestException) e; } else if (e instanceof FailingHttpStatusCodeException) { LOG.debug("Wrapping FailingHttpStatusCodeException in StepFailedException: " + e.getMessage()); final FailingHttpStatusCodeException he = (FailingHttpStatusCodeException) e; throw new StepFailedException("HTTP error " + he.getStatusCode(), he); } else if (e instanceof XPathException) { LOG.debug("Wrapping XPathException in StepFailedException: " + e.getMessage()); throw new StepFailedException(e.getMessage(), (Exception) e); } else if (e instanceof ScriptException) { final ScriptException se = (ScriptException) e; final HtmlPage page = se.getPage(); // should normally not be null but it happens in HtmlUnit 1.14 ;-( final StepFailedException sfe = new StepFailedException( "JavaScript error loading page " + (page != null ? page.getUrl().toString() : "") + ": " + se.getMessage(), se); sfe.addDetail("javascript error", se.getMessage()); sfe.addDetail("line", String.valueOf(se.getFailingLineNumber())); sfe.addDetail("javascript source", se.getScriptSourceCode()); final String failingLine = se.getFailingLine(); if (failingLine != null) sfe.addDetail("failing line", failingLine); // the javascript call stack final StringWriter stringWriter = new StringWriter(); final PrintWriter printWriter = new PrintWriter(stringWriter); se.printScriptStackTrace(printWriter); sfe.addDetail("javascript call stack", stringWriter.toString()); throw sfe; } else if (e instanceof SocketTimeoutException) { final SocketTimeoutException ste = (SocketTimeoutException) e; throw new StepFailedException("Server took to long to answer: " + ste.getMessage(), ste); } else if (e instanceof SAXException) { throw new StepExecutionException("Response is not well-formed: " + e.getMessage(), e); } else if (e instanceof XNIException) { // XNIException are not helpful, we need to extract the deeply // nested original exception final Throwable originalException = StepUtil.extractNestedException(e); throw new StepExecutionException("XNIException caused by " + originalException.getMessage(), originalException); } // TODO: See WT-194 should we catch java.io.IOException here and rethrow // as // StepFailed with message 'Server closed connection'? This would allow // retry around this situation throw new StepExecutionException("Unexpected exception caught: " + e.getClass().getName(), e); } }