/******************************************************************************* * 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 java.io.InputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; 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; /** * 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. * * @param errorDocument OData error document in XML format * @return created ODataErrorContext based on input stream content. * @throws EntityProviderException if an exception during read / deserialization occurs. */ public ODataErrorContext readError(final InputStream errorDocument) throws EntityProviderException { 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(final 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(final boolean codeFound, final 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(final XMLStreamReader reader) throws XMLStreamException { return notFinished(reader, FormatXml.M_ERROR); } private boolean notFinished(final XMLStreamReader reader, final 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 { StringBuilder sb = new StringBuilder(); while (notFinished(reader, FormatXml.M_INNER_ERROR)) { if (reader.hasName() && !FormatXml.M_INNER_ERROR.equals(reader.getLocalName())) { sb.append("<"); if (reader.isEndElement()) { sb.append("/"); } sb.append(reader.getLocalName()).append(">"); } else if (reader.isCharacters()) { sb.append(reader.getText()); } reader.next(); } errorContext.setInnerError(sb.toString()); } private void handleMessage(final XMLStreamReader reader, final 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(final XMLStreamReader reader, final ODataErrorContext errorContext) throws XMLStreamException { String code = reader.getElementText(); errorContext.setErrorCode(code); } private Locale getLocale(final String langValue) { return AVAILABLE_LOCALES.get(langValue); } }