mirror of https://github.com/apache/jclouds.git
Issue 812:add options to place xml source text to exception
This commit is contained in:
parent
72c1583ffd
commit
b3d6ad2ae8
|
@ -21,18 +21,20 @@ package org.jclouds.http.functions;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.io.Closeables.closeQuietly;
|
import static com.google.common.io.Closeables.closeQuietly;
|
||||||
|
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
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.InvocationContext;
|
||||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import org.xml.sax.SAXParseException;
|
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.Function;
|
||||||
import com.google.common.base.Throwables;
|
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
|
* 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>> {
|
public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext<ParseSax<T>> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private Logger logger = Logger.NULL;
|
||||||
|
|
||||||
private final XMLReader parser;
|
private final XMLReader parser;
|
||||||
private final HandlerWithResult<T> handler;
|
private final HandlerWithResult<T> handler;
|
||||||
private HttpRequest request;
|
private HttpRequest request;
|
||||||
|
@ -58,7 +64,6 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
|
||||||
<T> ParseSax<T> create(HandlerWithResult<T> handler);
|
<T> ParseSax<T> create(HandlerWithResult<T> handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ParseSax(XMLReader parser, HandlerWithResult<T> handler) {
|
public ParseSax(XMLReader parser, HandlerWithResult<T> handler) {
|
||||||
this.parser = checkNotNull(parser, "parser");
|
this.parser = checkNotNull(parser, "parser");
|
||||||
this.handler = checkNotNull(handler, "handler");
|
this.handler = checkNotNull(handler, "handler");
|
||||||
|
@ -71,34 +76,45 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
return addDetailsAndPropagate(from, e);
|
return addDetailsAndPropagate(from, e);
|
||||||
}
|
}
|
||||||
if (from.getStatusCode() >= 300)
|
InputStream is = null;
|
||||||
return convertStreamToStringAndParse(from);
|
|
||||||
InputStream is = from.getPayload().getInput();
|
|
||||||
try {
|
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));
|
return parse(new InputSource(is));
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return addDetailsAndPropagate(from, e);
|
return addDetailsAndPropagate(from, e);
|
||||||
} finally {
|
} finally {
|
||||||
closeQuietly(is);
|
Closeables.closeQuietly(is);
|
||||||
|
from.getPayload().release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private T convertStreamToStringAndParse(HttpResponse from) {
|
private T convertStreamToStringAndParse(HttpResponse response) {
|
||||||
|
String from = null;
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
return addDetailsAndPropagate(from, e);
|
return addDetailsAndPropagate(response, e, from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T parse(String from) {
|
public T parse(String from) {
|
||||||
try {
|
try {
|
||||||
checkNotNull(from, "xml string");
|
validateXml(from);
|
||||||
checkArgument(from.indexOf('<') >= 0, String.format("not an xml document [%s] ", from));
|
return doParse(new InputSource(new StringReader(from)));
|
||||||
} catch (RuntimeException e) {
|
} catch (Exception e) {
|
||||||
return addDetailsAndPropagate(null, 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) {
|
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) {
|
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();
|
StringBuilder message = new StringBuilder();
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
message.append("request: ").append(request.getRequestLine());
|
message.append("request: ").append(request.getRequestLine());
|
||||||
|
@ -144,15 +164,16 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
|
||||||
}
|
}
|
||||||
if (message.length() != 0)
|
if (message.length() != 0)
|
||||||
message.append("; ");
|
message.append("; ");
|
||||||
message.append(String.format("error at %d:%d in document %s", parseException.getColumnNumber(),
|
message.append(String.format("error at %d:%d in document %s", parseException.getColumnNumber(), parseException
|
||||||
parseException.getLineNumber(), systemId));
|
.getLineNumber(), systemId));
|
||||||
}
|
}
|
||||||
|
if (text != null)
|
||||||
|
message.append("; source:\n").append(text);
|
||||||
if (message.length() != 0) {
|
if (message.length() != 0) {
|
||||||
message.append("; cause: ").append(e.toString());
|
message.append("; cause: ").append(e.toString());
|
||||||
throw new RuntimeException(message.toString(), e);
|
throw new RuntimeException(message.toString(), e);
|
||||||
} else {
|
} else {
|
||||||
Throwables.propagate(e);
|
throw Throwables.propagate(e);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -167,7 +188,7 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public abstract static class HandlerWithResult<T> extends DefaultHandler implements
|
public abstract static class HandlerWithResult<T> extends DefaultHandler implements
|
||||||
InvocationContext<HandlerWithResult<T>> {
|
InvocationContext<HandlerWithResult<T>> {
|
||||||
private HttpRequest request;
|
private HttpRequest request;
|
||||||
|
|
||||||
protected HttpRequest getRequest() {
|
protected HttpRequest getRequest() {
|
||||||
|
|
|
@ -27,7 +27,9 @@ import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
||||||
import org.xml.sax.XMLReader;
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.Scopes;
|
import com.google.inject.Scopes;
|
||||||
|
|
||||||
|
@ -42,18 +44,27 @@ public class SaxParserModule extends AbstractModule {
|
||||||
bind(ParseSax.Factory.class).to(Factory.class).in(Scopes.SINGLETON);
|
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
|
@Inject
|
||||||
private SAXParserFactory factory;
|
Factory(SAXParserFactory factory, Injector i) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
|
||||||
public <T> ParseSax<T> create(HandlerWithResult<T> handler) {
|
public <T> ParseSax<T> create(HandlerWithResult<T> handler) {
|
||||||
SAXParser saxParser;
|
SAXParser saxParser;
|
||||||
try {
|
try {
|
||||||
saxParser = factory.newSAXParser();
|
saxParser = factory.newSAXParser();
|
||||||
XMLReader parser = saxParser.getXMLReader();
|
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) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,13 @@
|
||||||
<priority value="DEBUG" />
|
<priority value="DEBUG" />
|
||||||
<appender-ref ref="ASYNC" />
|
<appender-ref ref="ASYNC" />
|
||||||
</category>
|
</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">
|
<category name="jclouds.headers">
|
||||||
<priority value="DEBUG" />
|
<priority value="DEBUG" />
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
<appender-ref ref="ASYNCWIRE" />
|
||||||
|
|
|
@ -120,7 +120,13 @@
|
||||||
<priority value="DEBUG" />
|
<priority value="DEBUG" />
|
||||||
<appender-ref ref="ASYNC" />
|
<appender-ref ref="ASYNC" />
|
||||||
</category>
|
</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">
|
<category name="jclouds.headers">
|
||||||
<priority value="DEBUG" />
|
<priority value="DEBUG" />
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
<appender-ref ref="ASYNCWIRE" />
|
||||||
|
@ -132,7 +138,7 @@
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
<category name="jclouds.wire">
|
<category name="jclouds.wire">
|
||||||
<priority value="DEBUG" />
|
<priority value="TRACE" />
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
<appender-ref ref="ASYNCWIRE" />
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue