[OLINGO-196] Improved innerError handling (XML/JSON)
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/6a5fc6ce Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/6a5fc6ce Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/6a5fc6ce Branch: refs/heads/Olingo-129_PocJpaDataStore Commit: 6a5fc6ce6d41329360f63688fb8bc2ddf14c63b6 Parents: c8cdf37 Author: Michael Bolz <[email protected]> Authored: Wed Mar 26 15:10:37 2014 +0100 Committer: Michael Bolz <[email protected]> Committed: Wed Mar 26 15:10:37 2014 +0100 ---------------------------------------------------------------------- .../ep/consumer/JsonErrorDocumentConsumer.java | 49 +++++++- .../ep/consumer/XmlErrorDocumentConsumer.java | 31 ++++- .../consumer/JsonErrorDocumentConsumerTest.java | 44 +++++++ .../consumer/XmlErrorDocumentConsumerTest.java | 124 +++++++++++-------- 4 files changed, 188 insertions(+), 60 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6a5fc6ce/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumer.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumer.java index 9348b2a..5f5610b 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumer.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumer.java @@ -127,12 +127,49 @@ public class JsonErrorDocumentConsumer { } private void parseInnerError(final JsonReader reader, final ODataErrorContext errorContext) throws IOException { - // implementation for parse content as provided by JsonErrorDocumentProducer - String innerError = reader.nextString(); - errorContext.setInnerError(innerError); - // implementation for OData v2 Section 2.2.8.1.2 JSON Error Response - // (RFC4627 Section 2.2 -> https://www.ietf.org/rfc/rfc4627.txt)) - // currently not provided + if(reader.peek() == JsonToken.STRING) { + // implementation for parse content as provided by JsonErrorDocumentProducer + String innerError = reader.nextString(); + errorContext.setInnerError(innerError); + } else if(reader.peek() == JsonToken.BEGIN_OBJECT) { + // implementation for OData v2 Section 2.2.8.1.2 JSON Error Response + // (RFC4627 Section 2.2 -> https://www.ietf.org/rfc/rfc4627.txt)) + // currently partial provided + errorContext.setInnerError(readJson(reader)); + } + } + + + private String readJson(JsonReader reader) throws IOException { + StringBuilder sb = new StringBuilder(); + + while(reader.hasNext()) { + if(reader.peek() == JsonToken.NAME) { + if(sb.length() > 0) { + sb.append(","); + } + String name = reader.nextName(); + sb.append("\"").append(name).append("\"").append(":"); + } else if(reader.peek() == JsonToken.BEGIN_OBJECT) { + reader.beginObject(); + sb.append("{"); + sb.append(readJson(reader)); + sb.append("}"); + reader.endObject(); + } else if(reader.peek() == JsonToken.BEGIN_ARRAY) { + reader.beginArray(); + sb.append("["); + sb.append(readJson(reader)); + sb.append("]"); + reader.endArray(); + } else { + sb.append("\""); + sb.append(reader.nextString()); + sb.append("\""); + } + } + + return sb.toString(); } private void parseMessage(final JsonReader reader, final ODataErrorContext errorContext) http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6a5fc6ce/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumer.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumer.java index d584951..4b6b8ac 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumer.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumer.java @@ -136,16 +136,43 @@ public class XmlErrorDocumentConsumer { } private boolean notFinished(final XMLStreamReader reader) throws XMLStreamException { - boolean finished = reader.isEndElement() && FormatXml.M_ERROR.equals(reader.getLocalName()); + return notFinished(reader, FormatXml.M_ERROR); + } + + private boolean notFinished(final XMLStreamReader reader, String tagName) throws XMLStreamException { + boolean finished = reader.isEndElement() && tagName.equals(reader.getLocalName()); return !finished && reader.hasNext(); } private void handleInnerError(final XMLStreamReader reader, final ODataErrorContext errorContext) throws XMLStreamException { - String innerError = reader.getElementText(); + reader.next(); + String innerError = null; + if(reader.isCharacters()) { + innerError = reader.getText(); + } else if(reader.isStartElement()) { + innerError = handleComplexInnerError(reader); + } errorContext.setInnerError(innerError); } + private String handleComplexInnerError(XMLStreamReader reader) throws XMLStreamException { + StringBuilder sb = new StringBuilder(); + while(notFinished(reader, FormatXml.M_INNER_ERROR)) { + if(reader.hasName()) { + sb.append("<"); + if(reader.isEndElement()) { + sb.append("/"); + } + sb.append(reader.getLocalName()).append(">"); + } else if(reader.isCharacters()) { + sb.append(reader.getText()); + } + reader.next(); + } + return sb.toString(); + } + private void handleMessage(final XMLStreamReader reader, final ODataErrorContext errorContext) throws XMLStreamException { String lang = reader.getAttributeValue(Edm.NAMESPACE_XML_1998, FormatXml.XML_LANG); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6a5fc6ce/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumerTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumerTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumerTest.java index 204498d..9776289 100644 --- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumerTest.java +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/JsonErrorDocumentConsumerTest.java @@ -42,6 +42,15 @@ public class JsonErrorDocumentConsumerTest extends AbstractConsumerTest { "\"message\":{\"lang\":null,\"value\":\"Message\"}}}"; private static final String JSON_ERROR_DOCUMENT_INNER_ERROR = "{\"error\":{\"code\":\"ErrorCode\"," + "\"message\":{\"lang\":\"en-US\",\"value\":\"Message\"}, \"innererror\":\"Some InnerError\"}}"; + private static final String JSON_ERROR_DOCUMENT_INNER_ERROR_COMPLEX = "{\"error\":{\"code\":\"ErrorCode\"," + + "\"message\":{\"lang\":\"en-US\",\"value\":\"Message\"}, " + + "\"innererror\":{\"moreInner\":\"More Inner Error\"}}}"; + private static final String JSON_ERROR_DOCUMENT_INNER_ERROR_COMPLEX_OBJECT = "{\"error\":{\"code\":\"ErrorCode\"," + + "\"message\":{\"lang\":\"en-US\",\"value\":\"Message\"}, " + + "\"innererror\":{\"moreInner\":\"More Inner Error\",\"secondInner\":\"Second\"}}}"; + private static final String JSON_ERROR_DOCUMENT_INNER_ERROR_COMPLEX_ARRAY = "{\"error\":{\"code\":\"ErrorCode\"," + + "\"message\":{\"lang\":\"en-US\",\"value\":\"Message\"}, " + + "\"innererror\":{\"innerArray\":[\"More Inner Error\",\"Second\"]}}}"; private static final String JSON_ERROR_DOCUMENT_INVALID_JSON = "\"error\":{\"code\":\"ErrorCode\"," + "\"message\":{\"lang\":\"en-US\",\"value\":\"Message\"}}}"; /* error document with name 'locale' instead of 'lang' for message object */ @@ -90,6 +99,41 @@ public class JsonErrorDocumentConsumerTest extends AbstractConsumerTest { assertEquals("Wrong inner error", "Some InnerError", error.getInnerError()); } + @Test + public void innerErrorComplex() throws Exception { + InputStream in = StringHelper.encapsulate(JSON_ERROR_DOCUMENT_INNER_ERROR_COMPLEX); + ODataErrorContext error = jedc.readError(in); + + assertEquals("Wrong content type", "application/json", error.getContentType()); + assertEquals("Wrong message", "Message", error.getMessage()); + assertEquals("Wrong error code", "ErrorCode", error.getErrorCode()); + assertEquals("Wrong inner error", "{\"moreInner\":\"More Inner Error\"}", error.getInnerError()); + } + + @Test + public void innerErrorComplexObject() throws Exception { + InputStream in = StringHelper.encapsulate(JSON_ERROR_DOCUMENT_INNER_ERROR_COMPLEX_OBJECT); + ODataErrorContext error = jedc.readError(in); + + assertEquals("Wrong content type", "application/json", error.getContentType()); + assertEquals("Wrong message", "Message", error.getMessage()); + assertEquals("Wrong error code", "ErrorCode", error.getErrorCode()); + assertEquals("Wrong inner error", + "{\"moreInner\":\"More Inner Error\",\"secondInner\":\"Second\"}", error.getInnerError()); + } + + @Test + public void innerErrorComplexArray() throws Exception { + InputStream in = StringHelper.encapsulate(JSON_ERROR_DOCUMENT_INNER_ERROR_COMPLEX_ARRAY); + ODataErrorContext error = jedc.readError(in); + + assertEquals("Wrong content type", "application/json", error.getContentType()); + assertEquals("Wrong message", "Message", error.getMessage()); + assertEquals("Wrong error code", "ErrorCode", error.getErrorCode()); + assertEquals("Wrong inner error", + "{\"innerArray\":[\"More Inner Error\"\"Second\"]}", error.getInnerError()); + } + @Test(expected = EntityProviderException.class) public void invalidJson() throws EntityProviderException { InputStream in = StringHelper.encapsulate(JSON_ERROR_DOCUMENT_INVALID_JSON); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6a5fc6ce/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumerTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumerTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumerTest.java index 55ceb1d..ff3cc4f 100644 --- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumerTest.java +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumerTest.java @@ -6,9 +6,9 @@ * 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 @@ -32,66 +32,75 @@ import org.apache.olingo.odata2.testutil.helper.StringHelper; import org.junit.Test; /** - * + * */ public class XmlErrorDocumentConsumerTest extends AbstractConsumerTest { private static final String XML_ERROR_DOCUMENT_SIMPLE = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "\t<message xml:lang=\"en-US\">Message</message>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:lang=\"en-US\">Message</message>\n" + + "</error>"; private static final String XML_ERROR_DOCUMENT_NULL_LOCALE = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "\t<message xml:lang=\"\">Message</message>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:lang=\"\">Message</message>\n" + + "</error>"; private static final String XML_ERROR_DOCUMENT_INNER_ERROR = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "\t<message xml:lang=\"en-US\">Message</message>\n" + - "<innererror>Some InnerError</innererror>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:lang=\"en-US\">Message</message>\n" + + "<innererror>Some InnerError</innererror>\n" + + "</error>"; + private static final String XML_ERROR_DOCUMENT_INNER_ERROR_COMPLEX = + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:lang=\"en-US\">Message</message>\n" + + "<innererror>" + + "<moreInner>More Inner Error</moreInner>" + + "</innererror>\n" + + "</error>"; private static final String XML_ERROR_DOCUMENT_INVALID_XML = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</CODE>\n" + - "\t<message xml:lang=\"en-US\">Message</message>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</CODE>\n" + + "\t<message xml:lang=\"en-US\">Message</message>\n" + + "</error>"; /* error document with name 'locale' instead of 'lang' for message object */ private static final String XML_ERROR_DOCUMENT_UNKNOWN_CONTENT = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "\t<message xml:locale=\"en-US\">Message</message>\n" + - "\t<privateMessage>Secret</privateMessage>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:locale=\"en-US\">Message</message>\n" + + "\t<privateMessage>Secret</privateMessage>\n" + + "</error>"; /* error document without value for message object */ private static final String XML_ERROR_DOCUMENT_EMPTY_MESSAGE = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "\t<message xml:lang=\"en-US\" />\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:lang=\"en-US\" />\n" + + "</error>"; private static final String XML_ERROR_DOCUMENT_MISSING_MESSAGE = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "</error>"; private static final String XML_ERROR_DOCUMENT_MISSING_CODE = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<message xml:lang=\"en-US\">Message</message>\n" + - "</error>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<message xml:lang=\"en-US\">Message</message>\n" + + "</error>"; private static final String XML_ERROR_DOCUMENT_MISSING_ERROR = - "<?xml version='1.0' encoding='UTF-8'?>\n" + - "<errorForMe xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + - "\t<code>ErrorCode</code>\n" + - "\t<message xml:lang=\"en-US\">Message</message>\n" + - "</errorForMe>"; + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<errorForMe xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\n" + + "\t<code>ErrorCode</code>\n" + + "\t<message xml:lang=\"en-US\">Message</message>\n" + + "</errorForMe>"; private XmlErrorDocumentConsumer xedc = new XmlErrorDocumentConsumer(); @Test @@ -139,6 +148,17 @@ public class XmlErrorDocumentConsumerTest extends AbstractConsumerTest { assertEquals("Wrong inner error", "Some InnerError", error.getInnerError()); } + @Test + public void innerErrorComplex() throws Exception { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_INNER_ERROR_COMPLEX); + ODataErrorContext error = xedc.readError(in); + + assertEquals("Wrong content type", "application/xml", error.getContentType()); + assertEquals("Wrong message", "Message", error.getMessage()); + assertEquals("Wrong error code", "ErrorCode", error.getErrorCode()); + assertEquals("Wrong inner error", "<moreInner>More Inner Error</moreInner>", error.getInnerError()); + } + @Test(expected = EntityProviderException.class) public void invalidJson() throws EntityProviderException { InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_INVALID_XML); @@ -159,7 +179,7 @@ public class XmlErrorDocumentConsumerTest extends AbstractConsumerTest { fail("Expected exception was not thrown"); } catch (EntityProviderException e) { assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), - EntityProviderException.INVALID_STATE, e.getMessageReference()); + EntityProviderException.INVALID_STATE, e.getMessageReference()); throw e; } } @@ -195,7 +215,7 @@ public class XmlErrorDocumentConsumerTest extends AbstractConsumerTest { fail("Expected exception was not thrown"); } catch (EntityProviderException e) { assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), - EntityProviderException.INVALID_STATE, e.getMessageReference()); + EntityProviderException.INVALID_STATE, e.getMessageReference()); throw e; } } @@ -208,7 +228,7 @@ public class XmlErrorDocumentConsumerTest extends AbstractConsumerTest { fail("Expected exception was not thrown"); } catch (EntityProviderException e) { assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), - EntityProviderException.MISSING_PROPERTY, e.getMessageReference()); + EntityProviderException.MISSING_PROPERTY, e.getMessageReference()); assertTrue(e.getMessage().contains("code")); throw e; } @@ -222,7 +242,7 @@ public class XmlErrorDocumentConsumerTest extends AbstractConsumerTest { fail("Expected exception was not thrown"); } catch (EntityProviderException e) { assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), - EntityProviderException.MISSING_PROPERTY, e.getMessageReference()); + EntityProviderException.MISSING_PROPERTY, e.getMessageReference()); assertTrue(e.getMessage().contains("message")); throw e; }
