[OLINGO-1443]Streaming support for Media types

This commit is contained in:
ramya vasanth 2020-04-03 12:12:08 +05:30
parent 225bc34dd9
commit 1c8e5ce60b
9 changed files with 240 additions and 7 deletions

View File

@ -64,6 +64,23 @@ public class MediaITCase extends AbstractParamTecSvcITCase {
assertNotNull(media);
assertThat(IOUtils.toString(media), startsWith("<?xml"));
}
@Test
public void readMediaStream() throws Exception {
final ODataMediaRequest request = getClient().getRetrieveRequestFactory().getMediaRequest(
getClient().newURIBuilder(TecSvcConst.BASE_URI)
.appendEntitySetSegment("ESMediaStream").appendKeySegment(1).appendValueSegment().build());
assertNotNull(request);
final ODataRetrieveResponse<InputStream> response = request.execute();
assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
assertEquals("image/svg+xml", response.getContentType());
assertEquals("W/\"1\"", response.getETag());
InputStream media = response.getBody();
assertNotNull(media);
assertThat(IOUtils.toString(media), startsWith("<?xml"));
}
@Test
public void delete() {

View File

@ -0,0 +1,32 @@
/*
* 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.data;
public class EntityMediaObject {
private byte[] bytes;
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
public byte[] getBytes() {
return bytes;
}
}

View File

@ -21,6 +21,7 @@ package org.apache.olingo.server.api.serializer;
import java.io.InputStream;
import java.util.List;
import org.apache.olingo.commons.api.data.EntityMediaObject;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart;
@ -33,7 +34,15 @@ public interface FixedFormatSerializer {
* @param binary the binary data
*/
InputStream binary(byte[] binary) throws SerializerException;
/**
* Writes bytes to an Input stream
* @param mediaEntity bytes
* @return
* @throws SerializerException
*/
SerializerStreamResult mediaEntityStreamed(EntityMediaObject mediaEntity) throws SerializerException;
/**
* Writes a count into an InputStream as plain text.
* @param count the count

View File

@ -24,6 +24,7 @@ import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.EntityMediaObject;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.server.api.ODataContent;
@ -36,6 +37,7 @@ import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.core.serializer.SerializerStreamResultImpl;
import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl;
import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer;
import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer;
@ -55,6 +57,7 @@ public class ODataWritableContent implements ODataContent {
protected ServiceMetadata metadata;
protected EdmEntityType entityType;
protected EntityCollectionSerializerOptions options;
protected EntityMediaObject mediaEntity;
public StreamContent(EntityIterator iterator, EdmEntityType entityType, ServiceMetadata metadata,
EntityCollectionSerializerOptions options) {
@ -64,11 +67,22 @@ public class ODataWritableContent implements ODataContent {
this.options = options;
}
public StreamContent(EntityMediaObject mediaEntity) {
this.mediaEntity = mediaEntity;
}
protected abstract void writeEntity(EntityIterator entity, OutputStream outputStream) throws SerializerException;
protected abstract void writeBinary(EntityMediaObject mediaEntity, OutputStream outputStream)
throws SerializerException;
public void write(OutputStream out) {
try {
writeEntity(iterator, out);
if (mediaEntity == null) {
writeEntity(iterator, out);
} else {
writeBinary(mediaEntity, out);
}
} catch (SerializerException e) {
final ODataContentWriteErrorCallback errorCallback = options.getODataContentWriteErrorCallback();
if (errorCallback != null) {
@ -98,7 +112,34 @@ public class ODataWritableContent implements ODataContent {
throw new ODataRuntimeException("Failed entity serialization", e);
}
}
@Override
protected void writeBinary(EntityMediaObject mediaEntity,
OutputStream outputStream) throws SerializerException {
throw new ODataRuntimeException("Not Implemented in Entity Handling");
}
}
private static class StreamContentForMedia extends StreamContent {
private FixedFormatSerializerImpl fixedFormatSerializer;
public StreamContentForMedia(EntityMediaObject mediaEntity,
FixedFormatSerializerImpl fixedFormatSerializer) {
super(mediaEntity);
this.fixedFormatSerializer = fixedFormatSerializer;
}
protected void writeEntity(EntityIterator entity,
OutputStream outputStream) throws SerializerException {
throw new ODataRuntimeException("Not Implemented in Entity Handling");
}
@Override
protected void writeBinary(EntityMediaObject mediaEntity,
OutputStream outputStream) throws SerializerException {
fixedFormatSerializer.binaryIntoStreamed(mediaEntity, outputStream);
}
}
private static class StreamContentForXml extends StreamContent {
private ODataXmlSerializer xmlSerializer;
@ -119,6 +160,11 @@ public class ODataWritableContent implements ODataContent {
throw new ODataRuntimeException("Failed entity serialization", e);
}
}
protected void writeBinary(EntityMediaObject mediaEntity,
OutputStream outputStream) throws SerializerException {
throw new ODataRuntimeException("Not Implemented in XML Handling");
}
}
@Override
@ -140,6 +186,11 @@ public class ODataWritableContent implements ODataContent {
EntityCollectionSerializerOptions options) {
return new ODataWritableContentBuilder(iterator, entityType, serializer, metadata, options);
}
public static ODataWritableContentBuilder with(EntityMediaObject mediaEntity,
FixedFormatSerializerImpl fixedFormatSerializer) {
return new ODataWritableContentBuilder(mediaEntity, fixedFormatSerializer);
}
public static class WriteErrorContext implements ODataContentWriteErrorContext {
private ODataLibraryException exception;
@ -165,6 +216,8 @@ public class ODataWritableContent implements ODataContent {
private ServiceMetadata metadata;
private EdmEntityType entityType;
private EntityCollectionSerializerOptions options;
private FixedFormatSerializerImpl fixedFormatSerializer;
private EntityMediaObject mediaEntity;
public ODataWritableContentBuilder(EntityIterator entities, EdmEntityType entityType,
ODataSerializer serializer,
@ -176,6 +229,11 @@ public class ODataWritableContent implements ODataContent {
this.options = options;
}
public ODataWritableContentBuilder(EntityMediaObject mediaEntity, FixedFormatSerializerImpl fixedFormatSerializer) {
this.mediaEntity = mediaEntity;
this.fixedFormatSerializer = fixedFormatSerializer;
}
public ODataContent buildContent() {
if (serializer instanceof ODataJsonSerializer) {
StreamContent input = new StreamContentForJson(entities, entityType,
@ -185,6 +243,9 @@ public class ODataWritableContent implements ODataContent {
StreamContentForXml input = new StreamContentForXml(entities, entityType,
(ODataXmlSerializer) serializer, metadata, options);
return new ODataWritableContent(input);
} else if (fixedFormatSerializer instanceof FixedFormatSerializerImpl) {
StreamContent input = new StreamContentForMedia(mediaEntity, fixedFormatSerializer);
return new ODataWritableContent(input);
}
throw new ODataRuntimeException("No suitable serializer found");
}

View File

@ -19,10 +19,13 @@
package org.apache.olingo.server.core.serializer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.olingo.commons.api.data.EntityMediaObject;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.server.api.ODataResponse;
@ -31,6 +34,8 @@ import org.apache.olingo.server.api.serializer.BatchSerializerException;
import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
import org.apache.olingo.server.api.serializer.PrimitiveValueSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.core.ODataWritableContent;
public class FixedFormatSerializerImpl implements FixedFormatSerializer {
@ -38,6 +43,25 @@ public class FixedFormatSerializerImpl implements FixedFormatSerializer {
public InputStream binary(final byte[] binary) throws SerializerException {
return new ByteArrayInputStream(binary);
}
protected void binary(final EntityMediaObject mediaEntity,
OutputStream outputStream) throws SerializerException {
try {
outputStream.write(mediaEntity.getBytes());
} catch (IOException e) {
throw new SerializerException("IO Exception occured ", e, SerializerException.MessageKeys.IO_EXCEPTION);
}
}
public void binaryIntoStreamed(final EntityMediaObject mediaEntity,
final OutputStream outputStream) throws SerializerException {
binary(mediaEntity, outputStream);
}
@Override
public SerializerStreamResult mediaEntityStreamed(EntityMediaObject mediaEntity) throws SerializerException {
return ODataWritableContent.with(mediaEntity, this).build();
}
@Override
public InputStream count(final Integer count) throws SerializerException {

View File

@ -18,15 +18,19 @@
*/
package org.apache.olingo.server.core.serializer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.olingo.commons.api.data.EntityMediaObject;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
import org.apache.olingo.server.api.serializer.PrimitiveValueSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.junit.Test;
public class FixedFormatSerializerTest {
@ -53,4 +57,21 @@ public class FixedFormatSerializerTest {
assertEquals("42", IOUtils.toString(serializer.primitiveValue(type, 42,
PrimitiveValueSerializerOptions.with().nullable(true).build())));
}
@Test
public void binaryIntoStreamed() throws Exception {
EntityMediaObject mediaObject = new EntityMediaObject();
mediaObject.setBytes(new byte[] { 0x41, 0x42, 0x43 });
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
new FixedFormatSerializerImpl().binaryIntoStreamed(mediaObject, outputStream);
assertEquals(mediaObject.getBytes().length, outputStream.toByteArray().length);
}
@Test
public void mediaEntityStreamed() throws Exception {
EntityMediaObject mediaObject = new EntityMediaObject();
mediaObject.setBytes(new byte[] { 0x41, 0x42, 0x43 });
SerializerStreamResult result = serializer.mediaEntityStreamed(mediaObject);
assertNotNull(result.getODataContent());
}
}

View File

@ -34,7 +34,6 @@ import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import org.apache.olingo.commons.api.Constants;
@ -78,6 +77,7 @@ public class DataCreator {
data.put("ESAllKey", createESAllKey(edm, odata));
data.put("ESCompComp", createESCompComp(edm, odata));
data.put("ESMedia", createESMedia(edm, odata));
data.put("ESMediaStream", createESMediaStream(edm, odata));
data.put("ESKeyNav", createESKeyNav(edm, odata));
data.put("ESTwoKeyNav", createESTwoKeyNav(edm, odata));
data.put("ESCompCollComp", createESCompCollComp(edm, odata));
@ -1786,6 +1786,47 @@ public class DataCreator {
createOperations("ESMedia", entityCollection, EntityTypeProvider.nameETMedia);
return entityCollection;
}
private EntityCollection createESMediaStream(final Edm edm, final OData odata) {
EntityCollection entityCollection = new EntityCollection();
Entity entity = new Entity()
.addProperty(createPrimitive("PropertyInt16", (short) 1))
.addProperty(createPrimitive(DataProvider.MEDIA_PROPERTY_NAME, createImage("darkturquoise")));
entity.setMediaContentType("image/svg+xml");
entity.setMediaETag("W/\"1\"");
entity.getMediaEditLinks().add(buildMediaLink("ESMediaStream", "ESMediaStream(1)/$value"));
entityCollection.getEntities().add(entity);
entity = new Entity()
.addProperty(createPrimitive("PropertyInt16", (short) 2))
.addProperty(createPrimitive(DataProvider.MEDIA_PROPERTY_NAME, createImage("royalblue")));
entity.setMediaContentType("image/svg+xml");
entity.setMediaETag("W/\"2\"");
entity.getMediaEditLinks().add(buildMediaLink("ESMediaStream", "ESMediaStream(2)/$value"));
entityCollection.getEntities().add(entity);
entity = new Entity()
.addProperty(createPrimitive("PropertyInt16", (short) 3))
.addProperty(createPrimitive(DataProvider.MEDIA_PROPERTY_NAME, createImage("crimson")));
entity.setMediaContentType("image/svg+xml");
entity.setMediaETag("W/\"3\"");
entity.getMediaEditLinks().add(buildMediaLink("ESMediaStream", "ESMediaStream(3)/$value"));
entityCollection.getEntities().add(entity);
entity = new Entity()
.addProperty(createPrimitive("PropertyInt16", (short) 4))
.addProperty(createPrimitive(DataProvider.MEDIA_PROPERTY_NAME, createImage("black")));
entity.setMediaContentType("image/svg+xml");
entity.setMediaETag("W/\"4\"");
entity.getMediaEditLinks().add(buildMediaLink("ESMediaStream", "ESMediaStream(4)/$value"));
entityCollection.getEntities().add(entity);
setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETMedia));
createEntityId(edm, odata, "ESMediaStream", entityCollection);
createOperations("ESMediaStream", entityCollection, EntityTypeProvider.nameETMedia);
return entityCollection;
}
private byte[] createImage(final String color) {
return ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

View File

@ -36,6 +36,7 @@ import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.EntityMediaObject;
import org.apache.olingo.commons.api.data.Operation;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType;
@ -156,7 +157,15 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
getEdmEntitySet(uriInfo); // including checks
final Entity entity = readEntity(uriInfo);
final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource());
if (isMediaStreaming(edmEntitySet)) {
EntityMediaObject mediaEntity = new EntityMediaObject();
mediaEntity.setBytes(dataProvider.readMedia(entity));
response.setODataContent(odata.createFixedFormatSerializer()
.mediaEntityStreamed(mediaEntity).getODataContent());
} else {
response.setContent(odata.createFixedFormatSerializer().binary(dataProvider.readMedia(entity)));
}
response.setContent(odata.createFixedFormatSerializer().binary(dataProvider.readMedia(entity)));
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, entity.getMediaContentType());
@ -760,6 +769,10 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
return (ContainerProvider.ES_STREAM.equalsIgnoreCase(edmEntitySet.getName())||
ContainerProvider.ES_STREAM_SERVER_PAGINATION.equalsIgnoreCase(edmEntitySet.getName()));
}
private boolean isMediaStreaming(EdmEntitySet edmEntitySet) {
return (ContainerProvider.ES_MEDIA_STREAM.equalsIgnoreCase(edmEntitySet.getName()));
}
private SerializerResult serializeEntityCollection(final ODataRequest request, final EntityCollection
entityCollection, final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType,

View File

@ -54,6 +54,8 @@ public class ContainerProvider {
public static final String AIRT_BYTE_NINE_PARAM = "AIRTByteNineParam";
public static final String ES_STREAM = "ESStream";
public static final String ES_STREAM_SERVER_PAGINATION = "ESStreamServerSidePaging";
public static final String ES_MEDIA = "ESMedia";
public static final String ES_MEDIA_STREAM = "ESMediaStream";
private final CsdlEdmProvider prov;
@ -89,7 +91,8 @@ public class ContainerProvider {
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESCompCollAllPrim"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESCompComp"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESCompCollComp"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESMedia"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, ES_MEDIA));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, ES_MEDIA_STREAM));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESInvisible"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESServerSidePaging"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, ES_STREAM_SERVER_PAGINATION));
@ -347,7 +350,19 @@ public class ContainerProvider {
new CsdlAnnotation().setTerm(TermProvider.TERM_DATA.getFullQualifiedNameAsString()).setExpression(
new CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.Bool, "true"))));
} else if (name.equals("ESInvisible")) {
} else if (name.equals("ESMediaStream")) {
return new CsdlEntitySet()
.setName("ESMediaStream")
.setType(EntityTypeProvider.nameETMedia)
.setIncludeInServiceDocument(true)
.setAnnotations(Arrays.asList(
new CsdlAnnotation().setTerm("Core.Description")
.setExpression(new CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.String)
.setValue("Contains media entities")),
new CsdlAnnotation().setTerm(TermProvider.TERM_DATA.getFullQualifiedNameAsString()).setExpression(
new CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.Bool, "true"))));
} else if (name.equals("ESInvisible")) {
return new CsdlEntitySet()
.setName("ESInvisible")
.setIncludeInServiceDocument(false)