[OLINGO-196] Added XML implementation
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/9b1ba14b Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/9b1ba14b Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/9b1ba14b Branch: refs/heads/Olingo-129_PocJpaDataStore Commit: 9b1ba14ba5a12a9237904729b884836f77527128 Parents: 69e73b5 Author: Michael Bolz <[email protected]> Authored: Tue Mar 25 16:12:53 2014 +0100 Committer: Michael Bolz <[email protected]> Committed: Wed Mar 26 06:34:49 2014 +0100 ---------------------------------------------------------------------- .../olingo/odata2/core/commons/XmlHelper.java | 4 + .../ep/consumer/XmlErrorDocumentConsumer.java | 122 +++++++++- .../odata2/core/ep/ProviderFacadeImplTest.java | 18 ++ .../consumer/XmlErrorDocumentConsumerTest.java | 228 +++++++++++++++++++ 4 files changed, 371 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9b1ba14b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/commons/XmlHelper.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/commons/XmlHelper.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/commons/XmlHelper.java index f4bf49b..3efa87b 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/commons/XmlHelper.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/commons/XmlHelper.java @@ -32,6 +32,10 @@ public class XmlHelper { private static final String DEFAULT_CHARSET = "UTF-8"; public static XMLStreamReader createStreamReader(final Object content) throws EntityProviderException { + if (content == null) { + throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT + .addContent("Got not allowed NULL parameter for creation of XMLStreamReader.")); + } XMLStreamReader streamReader; try { XMLInputFactory factory = XMLInputFactory.newInstance(); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9b1ba14b/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 21f3f8c..d21cec4 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 @@ -19,14 +19,35 @@ package org.apache.olingo.odata2.core.ep.consumer; import java.io.InputStream; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import org.apache.olingo.odata2.api.edm.Edm; import org.apache.olingo.odata2.api.ep.EntityProviderException; import org.apache.olingo.odata2.api.processor.ODataErrorContext; +import org.apache.olingo.odata2.core.commons.ContentType; +import org.apache.olingo.odata2.core.commons.XmlHelper; +import org.apache.olingo.odata2.core.ep.util.FormatXml; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; /** * Consuming (read / deserialization) for OData error document in XML format. */ public class XmlErrorDocumentConsumer { + /** Map containing language code (language - country) to Locale mapping + * based on Locale.getAvailableLocales() + * */ + private final static Map<String, Locale> AVAILABLE_LOCALES = new HashMap<String, Locale>(); + static { + Locale[] locales = Locale.getAvailableLocales(); + for (Locale l : locales) { + AVAILABLE_LOCALES.put(l.getLanguage() + "-" + l.getCountry(), l); + } + } /** * Deserialize / read OData error document in ODataErrorContext. @@ -36,6 +57,105 @@ public class XmlErrorDocumentConsumer { * @throws EntityProviderException if an exception during read / deserialization occurs. */ public ODataErrorContext readError(final InputStream errorDocument) throws EntityProviderException { - throw new RuntimeException("Not yet implementedÃ"); + XMLStreamReader reader = null; + EntityProviderException cachedException = null; + + try { + reader = XmlHelper.createStreamReader(errorDocument); + return parserError(reader); + } catch (XMLStreamException e) { + cachedException = new EntityProviderException(EntityProviderException.INVALID_STATE.addContent( + e.getMessage()), e); + throw cachedException; + } catch (EntityProviderException e) { + cachedException = e; + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (XMLStreamException e) { + if (cachedException != null) { + throw cachedException; + } else { + throw new EntityProviderException( + EntityProviderException.EXCEPTION_OCCURRED.addContent( + e.getClass().getSimpleName()), e); + } + } + } + } + } + + private ODataErrorContext parserError(XMLStreamReader reader) throws XMLStreamException, EntityProviderException { + // read xml tag + reader.require(XMLStreamConstants.START_DOCUMENT, null, null); + reader.nextTag(); + + // read error tag + reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_ERROR); + + // read error data + boolean codeFound = false; + boolean messageFound = false; + ODataErrorContext errorContext = new ODataErrorContext(); + while(notFinished(reader)) { + reader.nextTag(); + if(reader.isStartElement()) { + String name = reader.getLocalName(); + if(FormatXml.M_CODE.equals(name)) { + codeFound = true; + handleCode(reader, errorContext); + } else if(FormatXml.M_MESSAGE.equals(name)) { + messageFound = true; + handleMessage(reader, errorContext); + } else if(FormatXml.M_INNER_ERROR.equals(name)) { + handleInnerError(reader, errorContext); + } else { + throw new EntityProviderException( + EntityProviderException.INVALID_CONTENT.addContent(name, FormatXml.M_ERROR)); + } + } + } + validate(codeFound, messageFound); + + errorContext.setContentType(ContentType.APPLICATION_XML.toContentTypeString()); + return errorContext; + } + + private void validate(boolean codeFound, boolean messageFound) throws EntityProviderException { + if (!codeFound) { + throw new EntityProviderException( + EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'code' property not found.'")); + } else if (!messageFound) { + throw new EntityProviderException( + EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'message' property not found.'")); + } + } + + private boolean notFinished(XMLStreamReader reader) throws XMLStreamException { + boolean finished = reader.isEndElement() && FormatXml.M_ERROR.equals(reader.getLocalName()); + return !finished && reader.hasNext(); + } + + private void handleInnerError(XMLStreamReader reader, ODataErrorContext errorContext) throws XMLStreamException { + String innerError = reader.getElementText(); + errorContext.setInnerError(innerError); + } + + private void handleMessage(XMLStreamReader reader, ODataErrorContext errorContext) throws XMLStreamException { + String lang = reader.getAttributeValue(Edm.NAMESPACE_XML_1998, FormatXml.XML_LANG); + errorContext.setLocale(getLocale(lang)); + String message = reader.getElementText(); + errorContext.setMessage(message); + } + + private void handleCode(XMLStreamReader reader, ODataErrorContext errorContext) throws XMLStreamException { + String code = reader.getElementText(); + errorContext.setErrorCode(code); + } + + private Locale getLocale(final String langValue) { + return AVAILABLE_LOCALES.get(langValue); } } http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9b1ba14b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImplTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImplTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImplTest.java index fdc8525..58577aa 100644 --- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImplTest.java +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImplTest.java @@ -249,6 +249,24 @@ public class ProviderFacadeImplTest extends AbstractConsumerTest { } @Test + public void readErrorDocumentXml() throws EntityProviderException { + ProviderFacadeImpl providerFacade = new ProviderFacadeImpl(); + String errorDoc = + "<?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>"; + ODataErrorContext errorContext = providerFacade.readErrorDocument(StringHelper.encapsulate(errorDoc), + ContentType.APPLICATION_XML.toContentTypeString()); + // + assertEquals("Wrong content type", "application/xml", errorContext.getContentType()); + assertEquals("Wrong message", "Message", errorContext.getMessage()); + assertEquals("Wrong error code", "ErrorCode", errorContext.getErrorCode()); + assertEquals("Wrong locale for lang", Locale.US, errorContext.getLocale()); + } + + @Test public void writeFeed() throws Exception { final EdmEntitySet entitySet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"); List<Map<String, Object>> propertiesList = new ArrayList<Map<String, Object>>(); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9b1ba14b/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 new file mode 100644 index 0000000..4106bcd --- /dev/null +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlErrorDocumentConsumerTest.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * 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.olingo.odata2.core.ep.consumer; + +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.processor.ODataErrorContext; +import org.apache.olingo.odata2.testutil.helper.StringHelper; +import org.junit.Test; + +import java.io.InputStream; +import java.util.Locale; + +import static org.junit.Assert.*; + +/** + * + */ +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>"; + 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>"; + 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>"; + 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>"; + /* 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>"; + /* 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>"; + 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>"; + 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>"; + 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>"; + private XmlErrorDocumentConsumer xedc = new XmlErrorDocumentConsumer(); + + @Test + public void simpleErrorDocument() throws Exception { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_SIMPLE); + 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 locale for lang", Locale.US, error.getLocale()); + } + + @Test + public void emptyMessage() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_EMPTY_MESSAGE); + + ODataErrorContext error = xedc.readError(in); + + assertEquals("Wrong content type", "application/xml", error.getContentType()); + assertEquals("Wrong message", "", error.getMessage()); + assertEquals("Wrong error code", "ErrorCode", error.getErrorCode()); + assertEquals("Wrong locale for lang", Locale.US, error.getLocale()); + } + + @Test + public void localeNull() throws Exception { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_NULL_LOCALE); + 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()); + assertNull("Expected NULL for locale", error.getLocale()); + } + + + @Test + public void innerError() throws Exception { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_INNER_ERROR); + 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", "Some InnerError", error.getInnerError()); + } + + @Test(expected = EntityProviderException.class) + public void invalidJson() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_INVALID_XML); + try { + xedc.readError(in); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals(EntityProviderException.INVALID_STATE, e.getMessageReference()); + throw e; + } + } + + @Test(expected = EntityProviderException.class) + public void invalidEmptyDocument() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(""); + try { + xedc.readError(in); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), + EntityProviderException.INVALID_STATE, e.getMessageReference()); + throw e; + } + } + + @Test(expected = EntityProviderException.class) + public void nullParameter() throws EntityProviderException { + try { + xedc.readError(null); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals(EntityProviderException.ILLEGAL_ARGUMENT, e.getMessageReference()); + throw e; + } + } + + @Test(expected = EntityProviderException.class) + public void invalidErrorDocumentUnknown() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_UNKNOWN_CONTENT); + try { + xedc.readError(in); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals(EntityProviderException.INVALID_CONTENT, e.getMessageReference()); + throw e; + } + } + + @Test(expected = EntityProviderException.class) + public void invalidErrorDocumentMissingError() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_MISSING_ERROR); + try { + xedc.readError(in); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), + EntityProviderException.INVALID_STATE, e.getMessageReference()); + throw e; + } + } + + @Test(expected = EntityProviderException.class) + public void invalidErrorDocumentMissingCode() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_MISSING_CODE); + try { + xedc.readError(in); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), + EntityProviderException.MISSING_PROPERTY, e.getMessageReference()); + assertTrue(e.getMessage().contains("code")); + throw e; + } + } + + @Test(expected = EntityProviderException.class) + public void invalidErrorDocumentMissingMessage() throws EntityProviderException { + InputStream in = StringHelper.encapsulate(XML_ERROR_DOCUMENT_MISSING_MESSAGE); + try { + xedc.readError(in); + fail("Expected exception was not thrown"); + } catch (EntityProviderException e) { + assertEquals("Got wrong exception: " + e.getMessageReference().getKey(), + EntityProviderException.MISSING_PROPERTY, e.getMessageReference()); + assertTrue(e.getMessage().contains("message")); + throw e; + } + } +}
