[OLINGO-349] ODataError serialization. Move to commons pending

This commit is contained in:
Christian Amend 2014-07-09 14:51:47 +02:00
parent 9469058193
commit 9403aeced7
7 changed files with 339 additions and 2 deletions

View File

@ -0,0 +1,88 @@
/*
* 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.server.api.serializer;
//TODO: Where to put this class
public class ODataError {
String code;
String message;
String target;
/**
* The value for the code name/value pair is a language-independent string. Its value is a service-defined error code.
* This code serves as a sub-status for the HTTP error code specified in the response. MAY be null.
* @return the error code as a string
*/
public String getCode() {
return code;
}
/**
* The value for the code name/value pair is a language-independent string. Its value is a service-defined error code.
* This code serves as a sub-status for the HTTP error code specified in the response. MAY be null.
* @param code
* @return this for method chaining
*/
public ODataError setCode(String code) {
this.code = code;
return this;
}
/**
* The value for the message name/value pair MUST be a human-readable, language-dependent representation of the error.
* MUST not be null
* @return the message string
*/
public String getMessage() {
return message;
}
/**
* The value for the message name/value pair MUST be a human-readable, language-dependent representation of the error.
* MUST not be null
* @param message
* @return this for method chaining
*/
public ODataError setMessage(String message) {
this.message = message;
return this;
}
/**
* The value for the target name/value pair is the target of the particular error (for example, the name of the
* property in error). MAY be null.
* @return the target string
*/
public String getTarget() {
return target;
}
/**
* The value for the target name/value pair is the target of the particular error (for example, the name of the
* property in error). MAY be null.
* @param target
* @return this for method chaining
*/
public ODataError setTarget(String target) {
this.target = target;
return this;
}
}

View File

@ -19,6 +19,7 @@
package org.apache.olingo.server.api.serializer;
import java.io.InputStream;
import java.util.List;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.Entity;
@ -38,4 +39,12 @@ public interface ODataSerializer {
InputStream entity(EdmEntityType edmEntityType, Entity entity, ContextURL contextURL);
InputStream entitySet(EdmEntitySet edmEntitySet, EntitySet entitySet, ContextURL contextURL);
/**
* Writes an ODataError into an InputStream
* @param error the main error
* @param details a list of details. MAY be null or empty.
* @return inputStream containing the OData formatted error
*/
InputStream error(ODataError error, List<ODataError> details);
}

View File

@ -69,6 +69,11 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -19,6 +19,7 @@
package org.apache.olingo.server.core.serializer;
import java.io.InputStream;
import java.util.List;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
@ -31,6 +32,7 @@ import org.apache.olingo.commons.api.data.EntitySet;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.server.api.serializer.ODataError;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import org.apache.olingo.server.core.serializer.xml.MetadataDocumentXmlSerializer;
@ -86,4 +88,9 @@ public class ODataXmlSerializerImpl implements ODataSerializer {
throw new ODataRuntimeException("Entityset serialization not implemented for XML format");
}
@Override
public InputStream error(ODataError error, List<ODataError> details) {
throw new ODataRuntimeException("error serialization not implemented for XML format");
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.server.core.serializer.json;
import java.io.IOException;
import java.util.List;
import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.server.api.serializer.ODataError;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
public class ODataErrorSerializer {
private static final String ERROR = "error";
private static final String CODE = "code";
private static final String MESSAGE = "message";
private static final String TARGET = "target";
private static final String DETAILS = "details";
public void writeErrorDocument(JsonGenerator json, ODataError error, List<ODataError> details) throws IOException {
if (error == null) {
throw new ODataRuntimeException("ODataError object MUST NOT be null!");
}
json.writeStartObject();
json.writeFieldName(ERROR);
json.writeStartObject();
writeODataError(json, error);
if(details != null){
json.writeArrayFieldStart(DETAILS);
for(ODataError detailedError : details){
json.writeStartObject();
writeODataError(json, detailedError);
json.writeEndObject();
}
json.writeEndArray();
}
json.writeEndObject();
json.writeEndObject();
}
private void writeODataError(JsonGenerator json, ODataError error) throws IOException, JsonGenerationException {
if (error.getCode() == null) {
json.writeNullField(CODE);
} else {
json.writeStringField(CODE, error.getCode());
}
if (error.getMessage() == null) {
json.writeNullField(MESSAGE);
} else {
json.writeStringField(MESSAGE, error.getMessage());
}
if (error.getTarget() != null) {
json.writeStringField(TARGET, error.getTarget());
}
}
}

View File

@ -26,6 +26,7 @@ import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.commons.api.data.*;
import org.apache.olingo.commons.api.edm.*;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.serializer.ODataError;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import org.slf4j.Logger;
@ -172,7 +173,7 @@ public class ODataJsonSerializer implements ODataSerializer {
writePrimitive(edmProperty, property, json);
} else if (property.isLinkedComplex()) {
writeComplexValue(edmProperty, property.asLinkedComplex().getValue(), json);
} else if(property.isComplex()) {
} else if (property.isComplex()) {
writeComplexValue(edmProperty, property.asComplex(), json);
} else {
throw new ODataRuntimeException("Property type not yet supported!");
@ -180,6 +181,7 @@ public class ODataJsonSerializer implements ODataSerializer {
}
}
private void writeCollection(EdmProperty edmProperty, Property property, JsonGenerator json)
throws IOException, EdmPrimitiveTypeException {
json.writeStartArray();
@ -243,7 +245,7 @@ public class ODataJsonSerializer implements ODataSerializer {
}
private void writeComplexValue(final EdmProperty edmProperty, final List<Property> properties,
JsonGenerator json) throws IOException, EdmPrimitiveTypeException {
JsonGenerator json) throws IOException, EdmPrimitiveTypeException {
final EdmComplexType type = (EdmComplexType) edmProperty.getType();
json.writeStartObject();
for (final String propertyName : type.getPropertyNames()) {
@ -261,4 +263,18 @@ public class ODataJsonSerializer implements ODataSerializer {
}
return null;
}
@Override
public InputStream error(ODataError error, List<ODataError> details) {
CircleStreamBuffer buffer = new CircleStreamBuffer();
try {
JsonGenerator json = new JsonFactory().createGenerator(buffer.getOutputStream());
ODataErrorSerializer ser = new ODataErrorSerializer();
ser.writeErrorDocument(json, error, details);
json.close();
} catch (final IOException e) {
throw new ODataRuntimeException(e);
}
return buffer.getInputStream();
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.server.core.serializer.json;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.commons.api.format.ODataFormat;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.serializer.ODataError;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.junit.Before;
import org.junit.Test;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
public class ODataErrorSerializerTest {
ODataSerializer ser;
@Before
public void before() {
ser = OData.newInstance().createSerializer(ODataFormat.JSON);
}
@Test
public void basicODataErrorNoCode() throws Exception {
ODataError error = new ODataError();
error.setMessage("ErrorMessage");
InputStream stream = ser.error(error, null);
String jsonString = IOUtils.toString(stream);
assertEquals("{\"error\":{\"code\":null,\"message\":\"ErrorMessage\"}}", jsonString);
}
@Test
public void basicODataErrorWithCode() throws Exception {
ODataError error = new ODataError();
error.setCode("Code").setMessage("ErrorMessage");
InputStream stream = ser.error(error, null);
String jsonString = IOUtils.toString(stream);
assertEquals("{\"error\":{\"code\":\"Code\",\"message\":\"ErrorMessage\"}}", jsonString);
}
@Test
public void basicODataErrorWithCodeAndTarget() throws Exception {
ODataError error = new ODataError();
error.setCode("Code").setMessage("ErrorMessage").setTarget("Target");
InputStream stream = ser.error(error, null);
String jsonString = IOUtils.toString(stream);
assertEquals("{\"error\":{\"code\":\"Code\",\"message\":\"ErrorMessage\",\"target\":\"Target\"}}", jsonString);
}
@Test(expected = ODataRuntimeException.class)
public void nullErrorResultsInException() throws Exception {
ser.error(null, null);
}
@Test
public void emptyDetailsList() throws Exception {
ODataError error = new ODataError();
error.setMessage("ErrorMessage");
InputStream stream = ser.error(error, new ArrayList<ODataError>());
String jsonString = IOUtils.toString(stream);
assertEquals("{\"error\":{\"code\":null,\"message\":\"ErrorMessage\",\"details\":[]}}", jsonString);
}
@Test
public void nothingSetAtODataErrorObject() throws Exception {
ODataError error = new ODataError();
InputStream stream = ser.error(error, null);
String jsonString = IOUtils.toString(stream);
assertEquals("{\"error\":{\"code\":null,\"message\":null}}", jsonString);
}
@Test
public void singleDetailNothingSet() throws Exception {
List<ODataError> details = new ArrayList<ODataError>();
details.add(new ODataError());
ODataError error = new ODataError();
InputStream stream = ser.error(error, details);
String jsonString = IOUtils.toString(stream);
assertEquals("{\"error\":{\"code\":null,\"message\":null,\"details\":[{\"code\":null,\"message\":null}]}}",
jsonString);
}
@Test
public void verifiedWithJacksonParser() throws Exception {
List<ODataError> details = new ArrayList<ODataError>();
details.add(new ODataError().setCode("detailCode").setMessage("detailMessage").setTarget("detailTarget"));
ODataError error = new ODataError().setCode("Code").setMessage("Message").setTarget("Target");
InputStream stream = ser.error(error, details);
JsonNode tree = new ObjectMapper().readTree(stream);
assertNotNull(tree);
tree = tree.get("error");
assertNotNull(tree);
assertEquals("Code", tree.get("code").textValue());
assertEquals("Message", tree.get("message").textValue());
assertEquals("Target", tree.get("target").textValue());
tree = tree.get("details");
assertNotNull(tree);
assertEquals(JsonNodeType.ARRAY, tree.getNodeType());
tree = tree.get(0);
assertNotNull(tree);
assertEquals("detailCode", tree.get("code").textValue());
assertEquals("detailMessage", tree.get("message").textValue());
assertEquals("detailTarget", tree.get("target").textValue());
}
}