blob: 8b5cc45be10887d69eaf847e19db5e11c6e95eb4 [file] [log] [blame]
/*******************************************************************************
* 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.client.core.ep.deserializer;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
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.ep.util.FormatJson;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
/**
* Consuming (read / deserialization) for OData error document in JSON format.
*/
public class JsonErrorDocumentDeserializer {
/** Default used charset for reader */
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String FOUND = "' found.";
/**
* Map containing language code (language - country) to Locale mapping
* based on Locale.getAvailableLocales()
* */
private static final 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 JSON 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 {
JsonReader reader = createJsonReader(errorDocument);
try {
return parseJson(reader);
} catch (IOException e) {
throw new EntityProviderException(
EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getMessage()), e);
}
}
/**
*
* @param reader
* @return ODataErrorContext
* @throws IOException
* @throws EntityProviderException
*/
private ODataErrorContext parseJson(final JsonReader reader) throws IOException, EntityProviderException {
ODataErrorContext errorContext;
if (reader.hasNext()) {
reader.beginObject();
String currentName = reader.nextName();
if (FormatJson.ERROR.equals(currentName)) {
errorContext = parseError(reader);
} else {
throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent(
"Invalid object with name '" + currentName + FOUND));
}
} else {
throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent(
"No content to parse found."));
}
errorContext.setContentType(ContentType.APPLICATION_JSON.toContentTypeString());
return errorContext;
}
/**
*
* @param reader
* @return ODataErrorContext
* @throws IOException
* @throws EntityProviderException
*/
private ODataErrorContext parseError(final JsonReader reader) throws IOException, EntityProviderException {
ODataErrorContext errorContext = new ODataErrorContext();
String currentName;
reader.beginObject();
boolean messageFound = false;
boolean codeFound = false;
while (reader.hasNext()) {
currentName = reader.nextName();
if (FormatJson.CODE.equals(currentName)) {
codeFound = true;
errorContext.setErrorCode(getValue(reader));
} else if (FormatJson.MESSAGE.equals(currentName)) {
messageFound = true;
parseMessage(reader, errorContext);
} else if (FormatJson.INNER_ERROR.equals(currentName)) {
parseInnerError(reader, errorContext);
} else {
throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent(
"Invalid name '" + currentName + FOUND));
}
}
if (!codeFound) {
throw new EntityProviderException(
EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'code' property not found.'"));
}
if (!messageFound) {
throw new EntityProviderException(
EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'message' property not found.'"));
}
reader.endObject();
return errorContext;
}
/**
*
* @param reader
* @param errorContext
* @throws IOException
*/
private void parseInnerError(final JsonReader reader, final ODataErrorContext errorContext) throws IOException {
JsonToken token = reader.peek();
if (token == JsonToken.STRING) {
// implementation for parse content as provided by JsonErrorDocumentProducer
String innerError = reader.nextString();
errorContext.setInnerError(innerError);
} else if (token == 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));
}
}
/**
*
* @param reader
* @return String
* @throws IOException
*/
private String readJson(final JsonReader reader) throws IOException {
StringBuilder sb = new StringBuilder();
while (reader.hasNext()) {
JsonToken token = reader.peek();
if (token == JsonToken.NAME) {
if (sb.length() > 0) {
sb.append(",");
}
String name = reader.nextName();
sb.append("\"").append(name).append("\"").append(":");
} else if (token == JsonToken.BEGIN_OBJECT) {
reader.beginObject();
sb.append("{")
.append(readJson(reader))
.append("}");
reader.endObject();
} else if (token == JsonToken.BEGIN_ARRAY) {
reader.beginArray();
sb.append("[")
.append(readJson(reader))
.append("]");
reader.endArray();
} else {
sb.append("\"")
.append(reader.nextString())
.append("\"");
}
}
return sb.toString();
}
/**
*
* @param reader
* @param errorContext
* @throws IOException
* @throws EntityProviderException
*/
private void parseMessage(final JsonReader reader, final ODataErrorContext errorContext)
throws IOException, EntityProviderException {
reader.beginObject();
boolean valueFound = false;
boolean langFound = false;
String currentName;
while (reader.hasNext()) {
currentName = reader.nextName();
if (FormatJson.LANG.equals(currentName)) {
langFound = true;
String langValue = getValue(reader);
if (langValue != null) {
errorContext.setLocale(getLocale(langValue));
}
} else if (FormatJson.VALUE.equals(currentName)) {
valueFound = true;
errorContext.setMessage(getValue(reader));
} else {
throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent("Invalid name '" +
currentName + FOUND));
}
}
if (!langFound) {
throw new EntityProviderException(
EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'lang' property not found.'"));
}
if (!valueFound) {
throw new EntityProviderException(
EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'value' property not found.'"));
}
reader.endObject();
}
private Locale getLocale(final String langValue) {
return AVAILABLE_LOCALES.get(langValue);
}
/**
* Read the string value from the JsonReader or 'null' if no value is available.
*
* @param reader to read from
* @return the string value or 'null'
* @throws IOException if an exception occurs
*/
private String getValue(final JsonReader reader) throws IOException {
JsonToken token = reader.peek();
if (JsonToken.NULL == token) {
reader.skipValue();
return null;
}
return reader.nextString();
}
private JsonReader createJsonReader(final InputStream in) throws EntityProviderException {
if (in == null) {
throw new EntityProviderException(EntityProviderException.INVALID_STATE
.addContent(("Got not supported NULL object as content to de-serialize.")));
}
try {
return new JsonReader(new InputStreamReader(in, DEFAULT_CHARSET));
} catch (final UnsupportedEncodingException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass()
.getSimpleName()), e);
}
}
}