Note:
The final source code can be found in the project git repository.
A detailed description how to checkout the tutorials can be found here.
This tutorial can be found in subdirectory /samples/tutorials/p10_media

You should read the previous tutorials first to have an idea how to read entities and entity collections. In addition the following code is based on the write tutorial merged with the navigation tutorial.

As a shortcut you should checkout the prepared tutorial project in the git repository in folder /samples/tutorials/p9_action_preparation.

Afterwards do a Deploy and run: it should be working. At this state you can perform CRUD operations and do navigations between products and categories.

As you can see, the XML tag EntityType has a property HasStream, which tells us that this entity has an associated media stream. Such an Entity consists of common properties like ID and Name and the media stream.

If you have read the previous tutorials, you should be familiar with the definition entity types. The only difference to regular (Non media enties) is, that they have a hasStream property. If this property is not provided it defaults to false. So add the following code to class DemoEdmProvider:

Further we have to create a new entity set. Add the following snipped to DemoEdmProvider.getEntitySet

@OverridepublicCsdlEntitySetgetEntitySet(FullQualifiedNameentityContainer,StringentitySetName){CsdlEntitySetentitySet=null;if(entityContainer.equals(CONTAINER)){if(entitySetName.equals(ES_PRODUCTS_NAME)){// Definition of entity set Products}elseif(entitySetName.equals(ES_CATEGORIES_NAME)){// Definition if entity set Categories}elseif(entitySetName.equals(ES_ADVERTISEMENTS_NAME)){entitySet=newCsdlEntitySet();entitySet.setName(ES_ADVERTISEMENTS_NAME);entitySet.setType(ET_ADVERTISEMENT_FQN);}}returnentitySet;}

In this tutorial, we will keep things simple. To store the value of media entities, we create a special property $value. Note this is not a valid OData Identifier.
All methods have to be implemented in class myservice.mynamespace.data.Storage

To read the content to a media entity, we simple return the value of the property $value.

If a client creates a new media entity, the body of the requet contains the content of the media entity instead the regular properties! So the other regular properties defaults to null. The Content Type of the media content must also be set.

The easiest part is to delete an media entity. The method deleteMediaEntity is delegated to the method deleteEntity(...).

@OverridepublicvoiddeleteMediaEntity(ODataRequestrequest,ODataResponseresponse,UriInfouriInfo)throwsODataApplicationException,ODataLibraryException{/* * In this tutorial, the content of the media entity is stored in a special property. * So no additional steps to delete the content of the media entity are necessary. * * A real service may store the content on the file system. So we have to take care to * delete external files too. * * DELETE request to /Advertisements(ID) will be dispatched to the deleteEntity(...) method * DELETE request to /Advertisements(ID)/$value will be dispatched to the deleteMediaEntity(...) method * * So it is a good idea handle deletes in a central place. */deleteEntity(request,response,uriInfo);}

Next the creation of media entites is implemented. First we fetch the addressed entity set and convert the body of the request to a byte array. Remember the whole body of the request contains the content of the media entity.

After that we call the data store to create the new media entity. The OData Specification tells us, that we have to set the location header to the edit URL of the entity. Since we do not support Prefer Headers we have to return the entity itself.

To keep things simple, our scenario do not support navigation to media entities. Because of this, the implementation to read a media entity is quite simple. First analayse the URI and fetch the entity. Than take the content of our specical property, serialize them and return the serialized content. The serializer converts the byte array to an InputStream.

@OverridepublicvoidreadMediaEntity(ODataRequestrequest,ODataResponseresponse,UriInfouriInfo,ContentTyperesponseFormat)throwsODataApplicationException,ODataLibraryException{// Since our scenario do not contain navigations from media entities. We can keep things simple and// check only the first resource path of the URI.finalUriResourcefirstResoucePart=uriInfo.getUriResourceParts().get(0);if(firstResoucePartinstanceofUriResourceEntitySet){finalEdmEntitySetedmEntitySet=Util.getEdmEntitySet(uriInfo);finalUriResourceEntitySeturiResourceEntitySet=(UriResourceEntitySet)firstResoucePart;finalEntityentity=storage.readEntityData(edmEntitySet,uriResourceEntitySet.getKeyPredicates());if(entity==null){thrownewODataApplicationException("Entity not found",HttpStatusCode.NOT_FOUND.getStatusCode(),Locale.ENGLISH);}finalbyte[]mediaContent=storage.readMedia(entity);finalInputStreamresponseContent=odata.createFixedFormatSerializer().binary(mediaContent);response.setStatusCode(HttpStatusCode.OK.getStatusCode());response.setContent(responseContent);response.setHeader(HttpHeader.CONTENT_TYPE,entity.getMediaContentType());}else{thrownewODataApplicationException("Not implemented",HttpStatusCode.BAD_REQUEST.getStatusCode(),Locale.ENGLISH);}}

Updating a media entity in our scenario is quite similar to read an entity. The first step is to analyse the URI, than fetch the entity from data store. Afer that we call the updateMediaEntity method. In our case we do not return any content. If we would return content, we must return the recently uploaded content of the media entity (OData Version 4.0 Part 1: Protocol).