[OLINGO-1182] Support Edm.Stream property for 4.0.1 in json format

This commit is contained in:
Archana Rai 2017-09-18 11:04:52 +05:30
parent 93af105892
commit 8a968923ba
18 changed files with 3147 additions and 67 deletions

View File

@ -59,6 +59,18 @@ public class BasicStreamITCase extends AbstractBaseTestITCase {
assertTrue(content.contains("\"PropertyString\":\"TEST 2->streamed\""));
}
@Test
public void streamESWithStreamJson() throws Exception {
URL url = new URL(SERVICE_URI + "ESWithStream?$expand=PropertyStream&$format=json");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HttpMethod.GET.name());
connection.connect();
assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode());
assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));
}
@Test
public void streamESStreamXml() throws Exception {
URL url = new URL(SERVICE_URI + "ESStream?$format=xml");

View File

@ -0,0 +1,61 @@
/*
* 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.commons.api;
/**
* Constant values related to the OData protocol.
*/
public interface IConstants {
public String getMetadata();
public String getType();
public String getId();
public String getReadLink();
public String getEditLink();
public String getContext();
public String getEtag();
public String getMediaEtag();
public String getMediaContentType();
public String getMediaReadLink();
public String getMediaEditLink();
public String getMetadataEtag();
public String getBind();
public String getAssociationLink();
public String getNavigationLink();
public String getCount();
public String getNextLink();
public String getDeltaLink();
}

View File

@ -0,0 +1,154 @@
/*
* 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.commons.api.constants;
import org.apache.olingo.commons.api.IConstants;
/**
* Constant values related to the OData protocol.
*/
public final class Constantsv00 implements IConstants{
// JSON stuff
public static final String JSON_METADATA = "odata.metadata";
public static final String JSON_TYPE = "@odata.type";
public static final String JSON_ID = "@odata.id";
public static final String JSON_READ_LINK = "@odata.readLink";
public static final String JSON_EDIT_LINK = "@odata.editLink";
public static final String JSON_CONTEXT = "@odata.context";
public static final String JSON_ETAG = "@odata.etag";
public static final String JSON_MEDIA_ETAG = "@odata.mediaEtag";
public static final String JSON_MEDIA_CONTENT_TYPE = "@odata.mediaContentType";
public static final String JSON_MEDIA_READ_LINK = "@odata.mediaReadLink";
public static final String JSON_MEDIA_EDIT_LINK = "@odata.mediaEditLink";
public static final String JSON_METADATA_ETAG = "@odata.metadataEtag";
public static final String JSON_BIND_LINK_SUFFIX = "@odata.bind";
public static final String JSON_ASSOCIATION_LINK = "@odata.associationLink";
public static final String JSON_NAVIGATION_LINK = "@odata.navigationLink";
public static final String JSON_COUNT = "@odata.count";
public static final String JSON_NEXT_LINK = "@odata.nextLink";
public static final String JSON_DELTA_LINK = "@odata.deltaLink";
@Override
public String getMetadata() {
return JSON_METADATA;
}
@Override
public String getType() {
return JSON_TYPE;
}
@Override
public String getId() {
return JSON_ID;
}
@Override
public String getReadLink() {
return JSON_READ_LINK;
}
@Override
public String getEditLink() {
return JSON_EDIT_LINK;
}
@Override
public String getContext() {
return JSON_CONTEXT;
}
@Override
public String getEtag() {
return JSON_ETAG;
}
@Override
public String getMediaEtag() {
return JSON_MEDIA_ETAG;
}
@Override
public String getMediaContentType() {
return JSON_MEDIA_CONTENT_TYPE;
}
@Override
public String getMediaReadLink() {
return JSON_MEDIA_READ_LINK;
}
@Override
public String getMediaEditLink() {
return JSON_MEDIA_EDIT_LINK;
}
@Override
public String getMetadataEtag() {
return JSON_METADATA_ETAG;
}
@Override
public String getBind() {
return JSON_BIND_LINK_SUFFIX;
}
@Override
public String getAssociationLink() {
return JSON_ASSOCIATION_LINK;
}
@Override
public String getNavigationLink() {
return JSON_NAVIGATION_LINK;
}
@Override
public String getCount() {
return JSON_COUNT;
}
@Override
public String getNextLink() {
return JSON_NEXT_LINK;
}
@Override
public String getDeltaLink() {
return JSON_DELTA_LINK;
}
}

View File

@ -0,0 +1,154 @@
/*
* 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.commons.api.constants;
import org.apache.olingo.commons.api.IConstants;
/**
* Constant values related to the OData protocol.
*/
public final class Constantsv01 implements IConstants{
// JSON stuff
public static final String JSON_METADATA = "metadata";
public static final String JSON_TYPE = "@type";
public static final String JSON_ID = "@id";
public static final String JSON_READ_LINK = "@readLink";
public static final String JSON_EDIT_LINK = "@editLink";
public static final String JSON_CONTEXT = "@context";
public static final String JSON_ETAG = "@etag";
public static final String JSON_MEDIA_ETAG = "@mediaEtag";
public static final String JSON_MEDIA_CONTENT_TYPE = "@mediaContentType";
public static final String JSON_MEDIA_READ_LINK = "@mediaReadLink";
public static final String JSON_MEDIA_EDIT_LINK = "@mediaEditLink";
public static final String JSON_METADATA_ETAG = "@metadataEtag";
public static final String JSON_BIND_LINK_SUFFIX = "@bind";
public static final String JSON_ASSOCIATION_LINK = "@associationLink";
public static final String JSON_NAVIGATION_LINK = "@navigationLink";
public static final String JSON_COUNT = "@count";
public static final String JSON_NEXT_LINK = "@nextLink";
public static final String JSON_DELTA_LINK = "@deltaLink";
@Override
public String getMetadata() {
return JSON_METADATA;
}
@Override
public String getType() {
return JSON_TYPE;
}
@Override
public String getId() {
return JSON_ID;
}
@Override
public String getReadLink() {
return JSON_READ_LINK;
}
@Override
public String getEditLink() {
return JSON_EDIT_LINK;
}
@Override
public String getContext() {
return JSON_CONTEXT;
}
@Override
public String getEtag() {
return JSON_ETAG;
}
@Override
public String getMediaEtag() {
return JSON_MEDIA_ETAG;
}
@Override
public String getMediaContentType() {
return JSON_MEDIA_CONTENT_TYPE;
}
@Override
public String getMediaReadLink() {
return JSON_MEDIA_READ_LINK;
}
@Override
public String getMediaEditLink() {
return JSON_MEDIA_EDIT_LINK;
}
@Override
public String getMetadataEtag() {
return JSON_METADATA_ETAG;
}
@Override
public String getBind() {
return JSON_BIND_LINK_SUFFIX;
}
@Override
public String getAssociationLink() {
return JSON_ASSOCIATION_LINK;
}
@Override
public String getNavigationLink() {
return JSON_NAVIGATION_LINK;
}
@Override
public String getCount() {
return JSON_COUNT;
}
@Override
public String getNextLink() {
return JSON_NEXT_LINK;
}
@Override
public String getDeltaLink() {
return JSON_DELTA_LINK;
}
}

View File

@ -79,6 +79,16 @@ public abstract class OData {
*/
public abstract ODataSerializer createSerializer(ContentType contentType) throws SerializerException;
/**
* Creates a new serializer object for rendering content in the specified format.
* Serializers are used in Processor implementations.
*
* @param contentType any format supported by Olingo (XML, JSON ...)
* @param versions any v4 version supported by Olingo (4.0, 4.01 ...)
*/
public abstract ODataSerializer createSerializer(ContentType contentType,
final List<String> versions) throws SerializerException;
/**
* Creates a new serializer object for rendering content in a fixed format, e.g., for binary output or multipart/mixed
* outpu.

View File

@ -51,6 +51,7 @@ public class SerializerException extends ODataLibraryException {
WRONG_PRIMITIVE_VALUE,
UNKNOWN_TYPE,
WRONG_BASE_TYPE,
UNSUPPORTED_OPERATION_TYPE,
/** parameter: encoding-name */
UNSUPPORTED_ENCODING;

View File

@ -21,6 +21,9 @@ package org.apache.olingo.server.core;
import java.util.Collection;
import java.util.List;
import org.apache.olingo.commons.api.constants.Constantsv00;
import org.apache.olingo.commons.api.constants.Constantsv01;
import org.apache.olingo.commons.api.IConstants;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider;
@ -71,7 +74,36 @@ public class ODataImpl extends OData {
|| ContentType.VALUE_ODATA_METADATA_MINIMAL.equalsIgnoreCase(metadata)
|| ContentType.VALUE_ODATA_METADATA_NONE.equalsIgnoreCase(metadata)
|| ContentType.VALUE_ODATA_METADATA_FULL.equalsIgnoreCase(metadata)) {
serializer = new ODataJsonSerializer(contentType);
serializer = new ODataJsonSerializer(contentType, new Constantsv00());
}
} else if (contentType.isCompatible(ContentType.APPLICATION_XML)
|| contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) {
serializer = new ODataXmlSerializer();
}
if (serializer == null) {
throw new SerializerException("Unsupported format: " + contentType.toContentTypeString(),
SerializerException.MessageKeys.UNSUPPORTED_FORMAT, contentType.toContentTypeString());
} else {
return serializer;
}
}
@Override
public ODataSerializer createSerializer(final ContentType contentType,
final List<String> versions) throws SerializerException {
ODataSerializer serializer = null;
IConstants constants = new Constantsv00();
if(versions!=null && versions.size()>0 && getMaxVersion(versions)>4){
constants = new Constantsv01() ;
}
if (contentType.isCompatible(ContentType.APPLICATION_JSON)) {
final String metadata = contentType.getParameter(ContentType.PARAMETER_ODATA_METADATA);
if (metadata == null
|| ContentType.VALUE_ODATA_METADATA_MINIMAL.equalsIgnoreCase(metadata)
|| ContentType.VALUE_ODATA_METADATA_NONE.equalsIgnoreCase(metadata)
|| ContentType.VALUE_ODATA_METADATA_FULL.equalsIgnoreCase(metadata)) {
serializer = new ODataJsonSerializer(contentType, constants);
}
} else if (contentType.isCompatible(ContentType.APPLICATION_XML)
|| contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) {

View File

@ -27,10 +27,10 @@ import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.server.api.ODataContent;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.ODataContentWriteErrorCallback;
import org.apache.olingo.server.api.ODataContentWriteErrorContext;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.SerializerException;
@ -181,7 +181,7 @@ public class ODataWritableContent implements ODataContent {
StreamContent input = new StreamContentForJson(entities, entityType,
(ODataJsonSerializer) serializer, metadata, options);
return new ODataWritableContent(input);
} else if (serializer instanceof ODataXmlSerializer) {
}else if (serializer instanceof ODataXmlSerializer) {
StreamContentForXml input = new StreamContentForXml(entities, entityType,
(ODataXmlSerializer) serializer, metadata, options);
return new ODataWritableContent(input);

View File

@ -28,7 +28,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.IConstants;
import org.apache.olingo.commons.api.constants.Constantsv00;
import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL;
@ -109,11 +113,20 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
private final boolean isIEEE754Compatible;
private final boolean isODataMetadataNone;
private final boolean isODataMetadataFull;
private IConstants constants;
public ODataJsonSerializer(final ContentType contentType, final IConstants constants) {
isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
this.constants = constants;
}
public ODataJsonSerializer(final ContentType contentType) {
isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
this.constants = new Constantsv00();
}
@Override
@ -208,6 +221,10 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} catch (DecoderException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} finally {
closeCircleStreamBufferOutput(outputStream, cachedException);
}
@ -255,6 +272,10 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} catch (DecoderException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
}
}
@ -284,6 +305,10 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} catch (DecoderException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} finally {
closeCircleStreamBufferOutput(outputStream, cachedException);
}
@ -301,12 +326,12 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final AbstractEntityCollection entitySet, final ExpandOption expand, Integer toDepth, final SelectOption select,
final boolean onlyReference, final Set<String> ancestors, String name, final JsonGenerator json)
throws IOException, SerializerException {
throws IOException, SerializerException, DecoderException {
json.writeStartArray();
for (final Entity entity : entitySet) {
if (onlyReference) {
json.writeStartObject();
json.writeStringField(Constants.JSON_ID, getEntityId(entity, entityType, name));
json.writeStringField(constants.getId(), getEntityId(entity, entityType, name));
json.writeEndObject();
} else {
writeEntity(metadata, entityType, entity, null, expand, toDepth, select, false, ancestors, name, json);
@ -354,7 +379,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
final ContextURL contextURL, final ExpandOption expand, Integer toDepth,
final SelectOption select, final boolean onlyReference, Set<String> ancestors,
String name, final JsonGenerator json)
throws IOException, SerializerException {
throws IOException, SerializerException, DecoderException {
boolean cycle = false;
if (expand != null) {
if (ancestors == null) {
@ -371,44 +396,45 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
writeMetadataETag(metadata, json);
}
if (entity.getETag() != null) {
json.writeStringField(Constants.JSON_ETAG, entity.getETag());
json.writeStringField(constants.getEtag(), entity.getETag());
}
if (entityType.hasStream()) {
if (entity.getMediaETag() != null) {
json.writeStringField(Constants.JSON_MEDIA_ETAG, entity.getMediaETag());
json.writeStringField(constants.getMediaEtag(), entity.getMediaETag());
}
if (entity.getMediaContentType() != null) {
json.writeStringField(Constants.JSON_MEDIA_CONTENT_TYPE, entity.getMediaContentType());
json.writeStringField(constants.getMediaContentType(), entity.getMediaContentType());
}
if (entity.getMediaContentSource() != null) {
json.writeStringField(Constants.JSON_MEDIA_READ_LINK, entity.getMediaContentSource().toString());
json.writeStringField(constants.getMediaReadLink(), entity.getMediaContentSource().toString());
}
if (entity.getMediaEditLinks() != null && !entity.getMediaEditLinks().isEmpty()) {
json.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, entity.getMediaEditLinks().get(0).getHref());
json.writeStringField(constants.getMediaEditLink(), entity.getMediaEditLinks().get(0).getHref());
}
}
}
if (cycle || onlyReference) {
json.writeStringField(Constants.JSON_ID, getEntityId(entity, entityType, name));
json.writeStringField(constants.getId(), getEntityId(entity, entityType, name));
} else {
final EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType());
if ((!isODataMetadataNone && !resolvedType.equals(entityType)) || isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" + entity.getType());
json.writeStringField(constants.getType(), "#" + entity.getType());
}
if ((!isODataMetadataNone && !areKeyPredicateNamesSelected(select, resolvedType)) || isODataMetadataFull) {
json.writeStringField(Constants.JSON_ID, getEntityId(entity, resolvedType, name));
json.writeStringField(constants.getId(), getEntityId(entity, resolvedType, name));
}
if (isODataMetadataFull) {
if (entity.getSelfLink() != null) {
json.writeStringField(Constants.JSON_READ_LINK, entity.getSelfLink().getHref());
json.writeStringField(constants.getReadLink(), entity.getSelfLink().getHref());
}
if (entity.getEditLink() != null) {
json.writeStringField(Constants.JSON_EDIT_LINK, entity.getEditLink().getHref());
json.writeStringField(constants.getEditLink(), entity.getEditLink().getHref());
}
}
writeProperties(metadata, resolvedType, entity.getProperties(), select, json);
writeExpandedStreamProperties(metadata, resolvedType, entity, expand, toDepth, ancestors, name, json);
writeNavigationProperties(metadata, resolvedType, entity, expand, toDepth, ancestors, name, json);
writeOperations(entity.getOperations(), json);
}
@ -501,16 +527,16 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
protected void writeNavigationProperties(final ServiceMetadata metadata,
final EdmStructuredType type, final Linked linked, final ExpandOption expand, final Integer toDepth,
final Set<String> ancestors, final String name, final JsonGenerator json)
throws SerializerException, IOException {
throws SerializerException, IOException, DecoderException {
if (isODataMetadataFull) {
for (final String propertyName : type.getNavigationPropertyNames()) {
final Link navigationLink = linked.getNavigationLink(propertyName);
if (navigationLink != null) {
json.writeStringField(propertyName + Constants.JSON_NAVIGATION_LINK, navigationLink.getHref());
json.writeStringField(propertyName + constants.getNavigationLink(), navigationLink.getHref());
}
final Link associationLink = linked.getAssociationLink(propertyName);
if (associationLink != null) {
json.writeStringField(propertyName + Constants.JSON_ASSOCIATION_LINK, associationLink.getHref());
json.writeStringField(propertyName + constants.getAssociationLink(), associationLink.getHref());
}
}
}
@ -555,12 +581,51 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
}
protected void writeExpandedStreamProperties(final ServiceMetadata metadata,
final EdmStructuredType type, final Linked linked, final ExpandOption expand, final Integer toDepth,
final Set<String> ancestors, final String name, final JsonGenerator json)
throws SerializerException, IOException, DecoderException {
if (ExpandSelectHelper.hasExpand(expand)) {
final ExpandItem expandAll = ExpandSelectHelper.getExpandAll(expand);
for (final String propertyName : type.getPropertyNames()) {
EdmProperty edmProperty = (EdmProperty) type.getProperty(propertyName);
if(isStreamProperty(edmProperty) ){
writeExpandedStreamProperty(expand, propertyName, edmProperty, linked, expandAll, json);
}
}
}
}
private void writeExpandedStreamProperty(ExpandOption expand, String propertyName, EdmProperty edmProperty,
Linked linked, ExpandItem expandAll, JsonGenerator json) throws SerializerException,
DecoderException, IOException {
final ExpandItem innerOptions = ExpandSelectHelper.getExpandItem(expand.getExpandItems(), propertyName);
if (innerOptions != null || expandAll != null) {
if(constants instanceof Constantsv00){
throw new SerializerException("Expand not supported for Stream Property Type!",
SerializerException.MessageKeys.UNSUPPORTED_OPERATION_TYPE, "expand", edmProperty.getName());
}
Entity entity = (Entity) linked;
final Property property = (Property) entity.getProperty(propertyName);
if((property == null || property.isNull()) && edmProperty.isNullable() == Boolean.FALSE ){
throw new SerializerException("Non-nullable property not present!",
SerializerException.MessageKeys.MISSING_PROPERTY, edmProperty.getName());
}
Link link = (Link) property.getValue();
Property stream = link.getInlineEntity().getProperty(propertyName);
Base64 decoder = new Base64(true);
byte[] decodedBytes = (byte[]) decoder.decode(stream.getValue());
json.writeStringField(propertyName, new String(decodedBytes));
}
}
protected void writeExpandedNavigationProperty(
final ServiceMetadata metadata, final EdmNavigationProperty property,
final Link navigationLink, final ExpandOption innerExpand,
Integer toDepth, final SelectOption innerSelect, final CountOption innerCount,
final boolean writeOnlyCount, final boolean writeOnlyRef, final Set<String> ancestors,
String name, final JsonGenerator json) throws IOException, SerializerException {
String name, final JsonGenerator json) throws IOException, SerializerException, DecoderException {
if (property.isCollection()) {
if (writeOnlyCount) {
@ -612,7 +677,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
json.writeFieldName(edmProperty.getName());
}
if (property == null || property.isNull()) {
if (edmProperty.isNullable() == Boolean.FALSE) {
if (edmProperty.isNullable() == Boolean.FALSE && !isStreamProperty) {
throw new SerializerException("Non-nullable property not present!",
SerializerException.MessageKeys.MISSING_PROPERTY, edmProperty.getName());
} else {
@ -635,7 +700,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
if (!isODataMetadataFull) {
return;
}
String typeName = edmProperty.getName() + Constants.JSON_TYPE;
String typeName = edmProperty.getName() + constants.getType();
final EdmType type = edmProperty.getType();
if (type.getKind() == EdmTypeKind.ENUM || type.getKind() == EdmTypeKind.DEFINITION) {
if (edmProperty.isCollection()) {
@ -720,7 +785,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
resolvedType = resolveComplexType(metadata, type, derivedName);
}
if (!isODataMetadataNone && !resolvedType.equals(type) || isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" +
json.writeStringField(constants.getType(), "#" +
resolvedType.getFullQualifiedName().getFullQualifiedNameAsString());
}
writeComplexValue(metadata, resolvedType, property.asComplex().getValue(), selectedPaths,
@ -768,7 +833,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
case COLLECTION_COMPLEX:
json.writeStartObject();
if (isODataMetadataFull || (!isODataMetadataNone && !derivedType.equals(type))) {
json.writeStringField(Constants.JSON_TYPE, "#" +
json.writeStringField(constants.getType(), "#" +
derivedType.getFullQualifiedName().getFullQualifiedNameAsString());
}
writeComplexValue(metadata, derivedType, ((ComplexValue) value).getValue(), selectedPaths, json);
@ -824,18 +889,18 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
Link stream = (Link)primitiveValue;
if (!isODataMetadataNone) {
if (stream.getMediaETag() != null) {
json.writeStringField(name+Constants.JSON_MEDIA_ETAG, stream.getMediaETag());
json.writeStringField(name+constants.getMediaEtag(), stream.getMediaETag());
}
if (stream.getType() != null) {
json.writeStringField(name+Constants.JSON_MEDIA_CONTENT_TYPE, stream.getType());
json.writeStringField(name+constants.getMediaContentType(), stream.getType());
}
}
if (isODataMetadataFull) {
if (stream.getRel() != null && stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) {
json.writeStringField(name+Constants.JSON_MEDIA_READ_LINK, stream.getHref());
json.writeStringField(name+constants.getMediaReadLink(), stream.getHref());
}
if (stream.getRel() == null || stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) {
json.writeStringField(name+Constants.JSON_MEDIA_EDIT_LINK, stream.getHref());
json.writeStringField(name+constants.getMediaEditLink(), stream.getHref());
}
}
}
@ -1035,7 +1100,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
resolvedType = resolveComplexType(metadata, type, property.getType());
}
if (!isODataMetadataNone && !resolvedType.equals(type) || isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" +
json.writeStringField(constants.getType(), "#" +
resolvedType.getFullQualifiedName().getFullQualifiedNameAsString());
}
writeOperations(property.getOperations(), json);
@ -1055,6 +1120,10 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} catch (DecoderException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} finally {
closeCircleStreamBufferOutput(outputStream, cachedException);
}
@ -1074,7 +1143,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
writeContextURL(contextURL, json);
writeMetadataETag(metadata, json);
if (isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#Collection("+type.getFullQualifiedName().getName()+")");
json.writeStringField(constants.getType(), "#Collection("+type.getFullQualifiedName().getName()+")");
}
writeOperations(property.getOperations(), json);
json.writeFieldName(Constants.VALUE);
@ -1112,7 +1181,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
writeContextURL(contextURL, json);
writeMetadataETag(metadata, json);
if (isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE,
json.writeStringField(constants.getType(),
"#Collection(" + type.getFullQualifiedName().getFullQualifiedNameAsString() + ")");
}
writeOperations(property.getOperations(), json);
@ -1147,7 +1216,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
json.writeStartObject();
writeContextURL(contextURL, json);
json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity));
json.writeStringField(constants.getId(), uriHelper.buildCanonicalURL(edmEntitySet, entity));
json.writeEndObject();
json.close();
@ -1186,7 +1255,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
json.writeArrayFieldStart(Constants.VALUE);
for (final Entity entity : entityCollection) {
json.writeStartObject();
json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity));
json.writeStringField(constants.getId(), uriHelper.buildCanonicalURL(edmEntitySet, entity));
json.writeEndObject();
}
json.writeEndArray();
@ -1210,7 +1279,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
void writeContextURL(final ContextURL contextURL, final JsonGenerator json) throws IOException {
if (!isODataMetadataNone && contextURL != null) {
json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString());
json.writeStringField(constants.getContext(), ContextURLBuilder.create(contextURL).toASCIIString());
}
}
@ -1219,7 +1288,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
&& metadata != null
&& metadata.getServiceMetadataETagSupport() != null
&& metadata.getServiceMetadataETagSupport().getMetadataETag() != null) {
json.writeStringField(Constants.JSON_METADATA_ETAG,
json.writeStringField(constants.getMetadataEtag(),
metadata.getServiceMetadataETagSupport().getMetadataETag());
}
}
@ -1228,9 +1297,9 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
throws IOException {
if (count != null) {
if (isIEEE754Compatible) {
json.writeStringField(propertyName + Constants.JSON_COUNT, String.valueOf(count));
json.writeStringField(propertyName + constants.getCount(), String.valueOf(count));
} else {
json.writeNumberField(propertyName + Constants.JSON_COUNT, count);
json.writeNumberField(propertyName + constants.getCount(), count);
}
}
}
@ -1239,7 +1308,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
throws IOException {
if (entitySet.getNext() != null) {
pagination = true;
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
json.writeStringField(constants.getNextLink(), entitySet.getNext().toASCIIString());
}else{
pagination = false;
}
@ -1248,7 +1317,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
void writeDeltaLink(final AbstractEntityCollection entitySet, final JsonGenerator json, boolean pagination)
throws IOException {
if (entitySet.getDeltaLink() != null && !pagination) {
json.writeStringField(Constants.JSON_DELTA_LINK, entitySet.getDeltaLink().toASCIIString());
json.writeStringField(constants.getDeltaLink(), entitySet.getDeltaLink().toASCIIString());
}
}
}

View File

@ -141,8 +141,10 @@ public abstract class ExpandSelectHelper {
continue;
}
final UriResource resource = item.getResourcePath().getUriResourceParts().get(0);
if (resource instanceof UriResourceNavigation
&& propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) {
if ((resource instanceof UriResourceNavigation
&& propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) ||
resource instanceof UriResourceProperty
&& propertyName.equals(((UriResourceProperty) resource).getProperty().getName())) {
return item;
}
}

View File

@ -23,10 +23,12 @@ import java.util.Map;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.uri.UriInfoKind;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
@ -42,6 +44,7 @@ import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
import org.apache.olingo.server.core.uri.UriResourceCountImpl;
import org.apache.olingo.server.core.uri.UriResourceEntitySetImpl;
import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
import org.apache.olingo.server.core.uri.UriResourcePrimitivePropertyImpl;
import org.apache.olingo.server.core.uri.UriResourceRefImpl;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
@ -154,27 +157,31 @@ public class ExpandParser {
}
}
}
final EdmStructuredType newReferencedType = typeCastSuffix != null ? typeCastSuffix
: (EdmStructuredType) lastPart.getType();
final boolean newReferencedIsCollection = lastPart.isCollection();
if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
if (tokenizer.next(TokenKind.REF)) {
resource.addResourcePart(new UriResourceRefImpl());
item.setIsRef(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
//For handling $expand for Stream property in v 4.01
if(lastPart instanceof UriResourcePrimitivePropertyImpl){
item.setResourcePath(resource);
}else{
final EdmStructuredType newReferencedType = typeCastSuffix != null ? typeCastSuffix
: (EdmStructuredType) lastPart.getType();
final boolean newReferencedIsCollection = lastPart.isCollection();
if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
if (tokenizer.next(TokenKind.REF)) {
resource.addResourcePart(new UriResourceRefImpl());
item.setIsRef(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
} else {
ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
resource.addResourcePart(new UriResourceCountImpl());
item.setCountPath(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
}
} else {
ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
resource.addResourcePart(new UriResourceCountImpl());
item.setCountPath(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, false);
}
} else {
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, false);
}
item.setResourcePath(resource);
}
item.setResourcePath(resource);
}
}
return item;
}
@ -204,7 +211,12 @@ public class ExpandParser {
final EdmNavigationProperty navigationProperty = type.getNavigationProperty(name);
if (navigationProperty == null) {
if (tokenizer.next(TokenKind.STAR)) {
//For handling $expand with Stream Properties in version 4.01
final EdmProperty streamProperty = (EdmProperty) type.getProperty(name);
if(streamProperty != null && streamProperty.getType() == EdmPrimitiveTypeFactory.
getInstance(EdmPrimitiveTypeKind.Stream)){
resource.addResourcePart(new UriResourcePrimitivePropertyImpl(streamProperty));
}else if (tokenizer.next(TokenKind.STAR)) {
item.setIsStar(true);
} else {
throw new UriParserSemanticException(

View File

@ -111,6 +111,7 @@ SerializerException.IO_EXCEPTION=An I/O exception occurred.
SerializerException.NULL_INPUT=The input 'null' is not allowed here.
SerializerException.NO_CONTEXT_URL=No context URL has been provided.
SerializerException.UNSUPPORTED_PROPERTY_TYPE=The type of the property '%1$s' is not yet supported.
SerializerException.UNSUPPORTED_OPERATION_TYPE=The '%1$s' operation is not supported for '%2$s'.
SerializerException.MISSING_DELTA_PROPERTY=The delta property '%1$s' is missing.
SerializerException.INCONSISTENT_PROPERTY_TYPE=An inconsistency has been detected in the type definition of property '%1$s'.
SerializerException.MISSING_PROPERTY=The non-nullable property '%1$s' is missing.

View File

@ -1379,7 +1379,9 @@ public class DataCreator {
Link readLink = new Link();
readLink.setRel(Constants.NS_MEDIA_READ_LINK_REL);
readLink.setHref("readLink");
Entity entity = new Entity();
entity.addProperty(createPrimitive("PropertyStream", createImage("darkturquoise")));
readLink.setInlineEntity(entity);
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
.addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, readLink)));
@ -1389,6 +1391,9 @@ public class DataCreator {
editLink.setHref("http://mediaserver:1234/editLink");
editLink.setMediaETag("eTag");
editLink.setType("image/jpeg");
entity = new Entity();
entity.addProperty(createPrimitive("PropertyStream", createImage("royalblue")));
editLink.setInlineEntity(entity);
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", (short) 7))

View File

@ -53,8 +53,8 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmSingleton;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
@ -859,4 +859,15 @@ public class DataProvider {
}
return null;
}
public byte[] readStreamProperty(Property property) {
Link link;
if(property!=null && property.getValue()!=null){
link = (Link)property.getValue();
if(link.getInlineEntity()!=null){
return (byte[]) link.getInlineEntity().getProperty(property.getName()).getValue();
}
}
return null;
}
}

View File

@ -709,7 +709,8 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,
final CountOption countOption, String id) throws ODataLibraryException {
return odata.createSerializer(requestedFormat).entityCollection(
return odata.createSerializer(requestedFormat, request.getHeaders(HttpHeader.ODATA_VERSION))
.entityCollection(
serviceMetadata,
edmEntityType,
entityCollection,
@ -836,7 +837,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
ContextURL contextUrl = isODataMetadataNone(requestedFormat) ? null :
getContextUrl(request.getRawODataPath(), edmEntitySet, edmEntityType, true, expand, null);
return odata.createSerializer(requestedFormat).entity(
return odata.createSerializer(requestedFormat, request.getHeaders(HttpHeader.ODATA_VERSION)).entity(
serviceMetadata,
edmEntityType,
entity,

View File

@ -27,6 +27,7 @@ import java.util.Locale;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Builder;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
@ -84,6 +85,8 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor
PrimitiveCollectionProcessor, CountPrimitiveCollectionProcessor,
ComplexProcessor, ComplexCollectionProcessor, CountComplexCollectionProcessor {
private static final Object EDMSTREAM = "Edm.Stream";
public TechnicalPrimitiveComplexProcessor(final DataProvider dataProvider,
final ServiceMetadata serviceMetadata) {
super(dataProvider, serviceMetadata);
@ -264,7 +267,15 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor
if (representationType == RepresentationType.VALUE) {
response.setContent(serializePrimitiveValue(property, edmProperty, (EdmPrimitiveType) type, returnType));
} else {
}else if(representationType == RepresentationType.PRIMITIVE && type.getFullQualifiedName()
.getFullQualifiedNameAsString().equals(EDMSTREAM)){
response.setContent(odata.createFixedFormatSerializer().binary(dataProvider.readStreamProperty(property)));
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, ((Link)property.getValue()).getType());
if (entity.getMediaETag() != null) {
response.setHeader(HttpHeader.ETAG, entity.getMediaETag());
}
}else {
final ExpandOption expand = uriInfo.getExpandOption();
final SelectOption select = uriInfo.getSelectOption();
final SerializerResult result = serializeProperty(entity, edmEntitySet, path, property, edmProperty,

View File

@ -1037,6 +1037,17 @@ public class ODataJsonSerializerTest {
+ "{\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"}]}";
Assert.assertEquals(expectedResult, resultString);
}
@Test(expected = SerializerException.class)
public void entityWithStreamExpand() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESWithStream");
final EntityCollection collection = data.readAll(edmEntitySet);
final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList(
ExpandSelectMock.mockExpandItem(edmEntitySet, "PropertyStream")));
serializer.entityCollection(metadata, edmEntitySet.getEntityType(), collection,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.expand(expand).build()).getContent();
}
@Test
public void entityMedia() throws Exception {