Issue 812:add options to place xml source text to exception

This commit is contained in:
Adrian Cole 2012-01-14 21:02:52 -08:00
parent 72c1583ffd
commit b3d6ad2ae8
4 changed files with 71 additions and 27 deletions

View File

@ -21,18 +21,20 @@ package org.jclouds.http.functions;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.io.Closeables.closeQuietly;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import javax.inject.Inject;
import javax.annotation.Resource;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.logging.Logger;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Strings2;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
@ -41,6 +43,7 @@ import org.xml.sax.helpers.DefaultHandler;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.io.Closeables;
/**
* This object will parse the body of an HttpResponse and return the result of type <T> back to the
@ -50,6 +53,9 @@ import com.google.common.base.Throwables;
*/
public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext<ParseSax<T>> {
@Resource
private Logger logger = Logger.NULL;
private final XMLReader parser;
private final HandlerWithResult<T> handler;
private HttpRequest request;
@ -58,7 +64,6 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
<T> ParseSax<T> create(HandlerWithResult<T> handler);
}
@Inject
public ParseSax(XMLReader parser, HandlerWithResult<T> handler) {
this.parser = checkNotNull(parser, "parser");
this.handler = checkNotNull(handler, "handler");
@ -71,34 +76,45 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
} catch (NullPointerException e) {
return addDetailsAndPropagate(from, e);
}
if (from.getStatusCode() >= 300)
return convertStreamToStringAndParse(from);
InputStream is = from.getPayload().getInput();
InputStream is = null;
try {
// debug is more normally set, so trace is more appropriate for
// something heavy like this
if (from.getStatusCode() >= 300 || logger.isTraceEnabled())
return convertStreamToStringAndParse(from);
is = from.getPayload().getInput();
return parse(new InputSource(is));
} catch (RuntimeException e) {
return addDetailsAndPropagate(from, e);
} finally {
closeQuietly(is);
Closeables.closeQuietly(is);
from.getPayload().release();
}
}
private T convertStreamToStringAndParse(HttpResponse from) {
private T convertStreamToStringAndParse(HttpResponse response) {
String from = null;
try {
return parse(Strings2.toStringAndClose(from.getPayload().getInput()));
from = new String(closeClientButKeepContentStream(response));
validateXml(from);
return doParse(new InputSource(new StringReader(from)));
} catch (Exception e) {
return addDetailsAndPropagate(from, e);
return addDetailsAndPropagate(response, e, from);
}
}
public T parse(String from) {
try {
checkNotNull(from, "xml string");
checkArgument(from.indexOf('<') >= 0, String.format("not an xml document [%s] ", from));
} catch (RuntimeException e) {
return addDetailsAndPropagate(null, e);
validateXml(from);
return doParse(new InputSource(new StringReader(from)));
} catch (Exception e) {
return addDetailsAndPropagate(null, e, from);
}
return parse(new InputSource(new StringReader(from)));
}
private void validateXml(String from) {
checkNotNull(from, "xml string");
checkArgument(from.indexOf('<') >= 0, String.format("not an xml document [%s] ", from));
}
public T parse(InputStream from) {
@ -127,6 +143,10 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
}
public T addDetailsAndPropagate(HttpResponse response, Exception e) {
return addDetailsAndPropagate(response, e, null);
}
public T addDetailsAndPropagate(HttpResponse response, Exception e, @Nullable String text) {
StringBuilder message = new StringBuilder();
if (request != null) {
message.append("request: ").append(request.getRequestLine());
@ -144,15 +164,16 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
}
if (message.length() != 0)
message.append("; ");
message.append(String.format("error at %d:%d in document %s", parseException.getColumnNumber(),
parseException.getLineNumber(), systemId));
message.append(String.format("error at %d:%d in document %s", parseException.getColumnNumber(), parseException
.getLineNumber(), systemId));
}
if (text != null)
message.append("; source:\n").append(text);
if (message.length() != 0) {
message.append("; cause: ").append(e.toString());
throw new RuntimeException(message.toString(), e);
} else {
Throwables.propagate(e);
return null;
throw Throwables.propagate(e);
}
}
@ -167,7 +188,7 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
* @author Adrian Cole
*/
public abstract static class HandlerWithResult<T> extends DefaultHandler implements
InvocationContext<HandlerWithResult<T>> {
InvocationContext<HandlerWithResult<T>> {
private HttpRequest request;
protected HttpRequest getRequest() {

View File

@ -27,7 +27,9 @@ import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.xml.sax.XMLReader;
import com.google.common.base.Throwables;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Scopes;
@ -42,18 +44,27 @@ public class SaxParserModule extends AbstractModule {
bind(ParseSax.Factory.class).to(Factory.class).in(Scopes.SINGLETON);
}
private static class Factory implements ParseSax.Factory {
static class Factory implements ParseSax.Factory {
private final SAXParserFactory factory;
private final Injector i;
@Inject
private SAXParserFactory factory;
Factory(SAXParserFactory factory, Injector i) {
this.factory = factory;
this.i = i;
}
public <T> ParseSax<T> create(HandlerWithResult<T> handler) {
SAXParser saxParser;
try {
saxParser = factory.newSAXParser();
XMLReader parser = saxParser.getXMLReader();
return new ParseSax<T>(parser, handler);
// TODO: switch to @AssistedInject
ParseSax<T> returnVal = new ParseSax<T>(parser, handler);
i.injectMembers(returnVal);
return returnVal;
} catch (Exception e) {
throw new RuntimeException(e);
throw Throwables.propagate(e);
}
}

View File

@ -120,7 +120,13 @@
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<!-- set to trace to get more info when parser fail -->
<category name="org.jclouds.http.functions.ParseSax">
<priority value="TRACE" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />

View File

@ -120,7 +120,13 @@
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<!-- set to trace to get more info when parser fail -->
<category name="org.jclouds.http.functions.ParseSax">
<priority value="TRACE" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
@ -132,7 +138,7 @@
</category>
<category name="jclouds.wire">
<priority value="DEBUG" />
<priority value="TRACE" />
<appender-ref ref="ASYNCWIRE" />
</category>