Author: mibo
Date: Tue May 19 09:19:54 2015
New Revision: 1680207

URL: http://svn.apache.org/r1680207
Log:
Part 3: Write

Added:
    olingo/site/trunk/content/doc/odata4/tutorials/write/
    olingo/site/trunk/content/doc/odata4/tutorials/write/tutorial_write.mdtext

Added: 
olingo/site/trunk/content/doc/odata4/tutorials/write/tutorial_write.mdtext
URL: 
http://svn.apache.org/viewvc/olingo/site/trunk/content/doc/odata4/tutorials/write/tutorial_write.mdtext?rev=1680207&view=auto
==============================================================================
--- olingo/site/trunk/content/doc/odata4/tutorials/write/tutorial_write.mdtext 
(added)
+++ olingo/site/trunk/content/doc/odata4/tutorials/write/tutorial_write.mdtext 
Tue May 19 09:19:54 2015
@@ -0,0 +1,390 @@
+Title:    Tutorial - Write service with Olingo V4
+Notice:    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.
+
+# How to build an OData Service with Olingo V4
+
+# Part 3: Write operations
+
+## Introduction
+
+This tutorial guides you through the steps required to write an OData Service 
based on the Olingo OData 4.0 Library for Java (based on current *Olingo 
4.0.0-beta-03* release which can be get via the 
[Download-Page](/doc/odata4/download.html)).
+
+In the first two tutorials ([Read 
Collection](/doc/odata4/tutorials/read/tutorial_read.html) and [Read 
Entity](/doc/odata4/tutorials/readep/tutorial_readep.html)), we’ve learned 
how to build a simple OData service that supports read operations for 
collection, single entity and property.
+
+In the present tutorial, will cover the write operations, which means creating 
an entity, modifying an existing entity and deleting an existing entity.
+
+Note:  
+The full implementation of the OData service as described in the present 
tutorial can be found in the [attached zip 
file](/doc/odata4/tutorials/write/sample/DemoService_Tutorial_Write.zip) 
([md5](/doc/odata4/tutorials/write/sample/DemoService_Tutorial_Write.zip.md5), 
[sha512](/doc/odata4/tutorials/write/sample/DemoService_Tutorial_Write.zip.sha512),
 [pgp](/doc/odata4/tutorials/write/sample/DemoService_Tutorial_Write.zip.asc)) 
that contains an Eclipse project that can be imported into your Eclipse 
workspace.
+
+Disclaimer:
+Again, in the present tutorial, will focus only on the relevant 
implementation, in order to keep the code small and simple.
+The sample code shouldn't be reused for advanced scenarios.
+
+---
+
+# 1. Prerequisites
+
+Same prerequisites as in [Tutorial Part 1: Read Entity 
Collection](http://olingo.apache.org/doc/odata4/tutorials/read/tutorial_read.html)
 and [Tutorial Part 2: Read 
Entity](http://olingo.apache.org/doc/odata4/tutorials/readep/tutorial_readep.html)
 as well as basic knowledge about the concepts presented in both tutorials.
+
+---
+
+# 2. Preparation
+
+Follow [Tutorial Part 1: Read Entity 
Collection](http://olingo.apache.org/doc/odata4/tutorials/read/tutorial_read.html)
 and [Tutorial Part 2: Read 
Entity](http://olingo.apache.org/doc/odata4/tutorials/readep/tutorial_readep.html)
 or as shortcut import the project attached to *Tutorial Part 2* into your 
Eclipse workspace.
+
+Afterwards do a *Deploy and run*: it should be working.
+
+---
+
+# 3. Implementation
+
+In our sample scenario, we want to create a product, to be added to the list 
of available products that we maintain in our database-mock.
+This product that we want to create will have a name and a description that 
the user of our service will specify in his HTTP request.
+The Olingo library takes this user request, serializes the request body and 
invokes the corresponding method of our processor class.
+
+In the previous tutorial 2, we’ve already implemented the `EntityProcessor` 
interface and registered our class in the servlet, but we have not written the 
implementation for the callback methods that are responsible for the write 
operations.
+This is what we are going to do in the below sections.
+
+## 3.1. Implement the createEntity(...) method
+
+Open the class `myservice.mynamespace.service.DemoEntityProcessor`  
+Go to the method `createEntity(...)`  
+The method body should be empty, otherwise delete any content.
+
+**Now, how to implement the method?**  
+Basically, we have to do the same that we did in the `readEntity(...)` method, 
but the other way ‘round.  
+In the `createEntity(...)` method, we have to retrieve the payload from the 
request and then write it to our mock-database.  
+Furthermore, we have to return the created entity in the response payload.
+
+Again, we can divide our work into 4 steps:
+
+  1. Analyze the URI
+  1. Handle data in backend
+  1. Serialize
+  1. Configure the response
+
+
+**In detail**
+
+We have to keep in mind that -for creation - the URL that is executed in our 
example is the following:
+
+    http://localhost:8080/DemoService/DemoService.svc/Products
+
+It is executed as POST request and contains a request body which looks as 
follows:
+
+    :::json
+    {
+      "ID":4,
+      "Name":"Gamer Mouse",
+      "Description":"optical mouse - gamer edition"
+    }
+
+
+**Steps**
+
+  1. In the implementation, we have to first retrieve the `EntityCollection` 
and `EntityType` metadata from the `UriInfo` object.
+  1. The next step is to create the data in our backend.  
+  For this purpose, we have to retrieve the data from the HTTP request 
payload.  
+  We get the payload from the `ODataRequest` instance as `InputStream`, which 
can then be deserialized.
+  Our `Storage` class is responsible for creating the new product in the 
backend.
+  And for returning the newly created instance.  
+  The reason is that our OData service has to return the newly created entity 
in the response body.  
+  1. From now on the procedure is the same like in the `readEntity(...)` method
+  1. The only difference is the status code, that has to be set to **201 - 
created** in case of success
+
+Please find below the sample code for the *createEntity()* method
+
+    :::java
+    public void createEntity(ODataRequest request, ODataResponse response, 
UriInfo uriInfo,
+         ContentType requestFormat, ContentType responseFormat)
+        throws ODataApplicationException, DeserializerException, 
SerializerException {
+
+      // 1. Retrieve the entity type from the URI
+      EdmEntitySet edmEntitySet = Util.getEdmEntitySet(uriInfo);
+      EdmEntityType edmEntityType = edmEntitySet.getEntityType();
+
+      // 2. create the data in backend
+      // 2.1. retrieve the payload from the POST request for the entity to 
create and deserialize it
+      InputStream requestInputStream = request.getBody();
+      ODataFormat requestODataFormat = 
ODataFormat.fromContentType(requestFormat);
+      ODataDeserializer deserializer = 
this.odata.createDeserializer(requestODataFormat);
+      DeserializerResult result = deserializer.entity(requestInputStream, 
edmEntityType);
+      Entity requestEntity = result.getEntity();
+      // 2.2 do the creation in backend, which returns the newly created entity
+      Entity createdEntity = storage.createEntityData(edmEntitySet, 
requestEntity);
+
+      // 3. serialize the response (we have to return the created entity)
+      ContextURL contextUrl = 
ContextURL.with().entitySet(edmEntitySet).build();
+        // expand and select currently not supported
+      EntitySerializerOptions options = 
EntitySerializerOptions.with().contextURL(contextUrl).build();
+
+      ODataFormat oDataFormat = ODataFormat.fromContentType(responseFormat);
+      ODataSerializer serializer = this.odata.createSerializer(oDataFormat);
+      SerializerResult serializedResponse = serializer.entity(serviceMetadata, 
edmEntityType, createdEntity, options);
+
+      //4. configure the response object
+      response.setContent(serializedResponse.getContent());
+      response.setStatusCode(HttpStatusCode.CREATED.getStatusCode());
+      response.setHeader(HttpHeader.CONTENT_TYPE, 
responseFormat.toContentTypeString());
+    }
+
+
+
+
+## 3.2. Implement the updateEntity(...) method
+
+Example URL
+
+    http://localhost:8080/DemoService/DemoService.svc/Products(3)
+
+Example request body:
+
+    :::json
+    {
+      "ID":3,
+      "Name":"Ergo Screen updated Name",
+      "Description":"updated description"
+    }
+
+
+The `updateEntity(...)` method is similar.  
+Again, we have to retrieve the payload from the HTTP request and use it for 
modifying the data in backend.
+The difference is that case of update operation, the OData service is not 
expected to return any response payload. So we can skip the serialize-step and 
simply set the HTTP status code to **204 – no content**
+
+    :::java
+    public void updateEntity(ODataRequest request, ODataResponse response, 
UriInfo uriInfo,
+    ContentType requestFormat, ContentType responseFormat)
+        throws ODataApplicationException, DeserializerException, 
SerializerException {
+
+      // 1. Retrieve the entity set which belongs to the requested entity
+      List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
+      // Note: only in our example we can assume that the first segment is the 
EntitySet
+      UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) 
resourcePaths.get(0);
+      EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
+      EdmEntityType edmEntityType = edmEntitySet.getEntityType();
+
+      // 2. update the data in backend
+      // 2.1. retrieve the payload from the PUT request for the entity to be 
updated
+      InputStream requestInputStream = request.getBody();
+      ODataFormat requestODataFormat = 
ODataFormat.fromContentType(requestFormat);
+      ODataDeserializer deserializer = 
this.odata.createDeserializer(requestODataFormat);
+      DeserializerResult result = deserializer.entity(requestInputStream, 
edmEntityType);
+      Entity requestEntity = result.getEntity();
+      // 2.2 do the modification in backend
+      List<UriParameter> keyPredicates = 
uriResourceEntitySet.getKeyPredicates();
+      // Note that this updateEntity()-method is invoked for both PUT or PATCH 
operations
+      HttpMethod httpMethod = request.getMethod();
+      storage.updateEntityData(edmEntitySet, keyPredicates, requestEntity, 
httpMethod);
+
+      //3. configure the response object
+      response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
+    }
+
+
+In case of update, we have to consider the following:  
+The update of an entity can be realized in 2 ways: either a **PATCH** or a 
**PUT** request.  
+(See the online specification in section [11.4.3 Update an 
Entity](http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part1-protocol.html)
 for more details.
+For both HTTP methods, our `updateEntity(...)` will be invoked.  
+But we have to treat the data-modification differently.  
+Therefore, we have to first retrieve the used HTTP method and in the 
backend-logic, we have to distinguish between **PATCH** and **PUT**.  
+The difference becomes relevant only in case if the user doesn’t send all 
the properties in the request body.  
+
+Example: if we modify the above example request body to look as follows:  
+
+    :::json
+    {
+      "Description":"updated description"
+    }
+
+
+Note that in this case, only one of three properties is sent in the request 
body.
+
+  - If the HTTP method is **PATCH**:  
+    The value of the *Description* property is updated in the backend.  
+    The values of the other properties remain untouched.
+  - If the HTTP method is **PUT**:  
+    The value of the *Description* property is updated in the backend.  
+    The value of the other properties is set to null (exception: key 
properties can never be null).  
+
+
+So let’s have a look at our sample implementation in the `Storage` class 
(see below for full sample code and also see the attached zip file containing 
the whole sample project)
+
+    :::java
+    private void updateProduct(EdmEntityType edmEntityType, List<UriParameter> 
keyParams, Entity entity, HttpMethod httpMethod)
+                                throws ODataApplicationException{
+
+      Entity productEntity = getProduct(edmEntityType, keyParams);
+      if(productEntity == null){
+        throw new ODataApplicationException("Entity not found",
+                            HttpStatusCode.NOT_FOUND.getStatusCode(), 
Locale.ENGLISH);
+      }
+
+      // loop over all properties and replace the values with the values of 
the given payload
+      // Note: ignoring ComplexType, as we don't have it in our odata model
+      List<Property> existingProperties = productEntity.getProperties();
+      for(Property existingProp : existingProperties){
+        String propName = existingProp.getName();
+
+        // ignore the key properties, they aren't updateable
+        if(isKey(edmEntityType, propName)){
+          continue;
+        }
+
+        Property updateProperty = entity.getProperty(propName);
+        // the request payload might not consider ALL properties, so it can be 
null
+        if(updateProperty == null){
+          // if a property has NOT been added to the request payload
+          // depending on the HttpMethod, our behavior is different
+          if(httpMethod.equals(HttpMethod.PATCH)){
+            // in case of PATCH, the existing property is not touched
+            continue; // do nothing
+          }else if(httpMethod.equals(HttpMethod.PUT)){
+            // in case of PUT, the existing property is set to null
+            existingProp.setValue(existingProp.getValueType(), null);
+            continue;
+          }
+        }
+
+        // change the value of the properties
+        existingProp.setValue(existingProp.getValueType(), 
updateProperty.getValue());
+      }
+    }
+
+
+## 3.3. Implement the deleteEntity(...) method
+
+In case of **DELETE** operation, the URL is the same like for the **GET** 
operation, but the request body is empty.
+
+Example URL:
+
+    http://localhost:8080/DemoService/DemoService.svc/Products(3)
+
+The implementation is rather simple:  
+
+  - As usual, determine the entity set.  
+  - Delete the data in backend.  
+  - Configure the response object with the proper status code **204 – no 
content**.  
+
+        :::java
+        public void deleteEntity(ODataRequest request, ODataResponse response, 
UriInfo uriInfo)
+                                throws ODataApplicationException {
+
+          // 1. Retrieve the entity set which belongs to the requested entity
+          List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
+          // Note: only in our example we can assume that the first segment is 
the EntitySet
+          UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) 
resourcePaths.get(0);
+          EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
+
+          // 2. delete the data in backend
+          List<UriParameter> keyPredicates = 
uriResourceEntitySet.getKeyPredicates();
+          storage.deleteEntityData(edmEntitySet, keyPredicates);
+
+          //3. configure the response object
+          response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
+        }
+
+
+
+## 3.4. Run the service
+
+After building and deploying the project, we can invoke our OData service.
+
+In order to test the write operations of our OData service, we need a tool 
that is able to execute the following required HTTP requests: **POST**, 
**PUT**, **PATCH**, **DELETE**  
+This is usually done with any REST client tool that can be installed into the 
browser of your choice.  
+
+Examples:  
+    - Firefox: “RESTClient, a debugger for RESTful web services”  
+    - Chrome: “Advanced REST client”  
+
+The following sections provide examples for executing the requests:
+
+### 3.4.1. Example for **CREATE**:
+
+  - URL: <http://localhost:8080/DemoService/DemoService.svc/Products>  
+  - HTTP verb: **POST**
+  - Header: `Content-Type: application/json; odata.metadata=minimal`  
+  - Request body:  
+
+        :::json
+        {  
+          "ID":6,  
+          "Name":"Gamer Mouse",  
+          "Description":"optical mouse - gamer edition"  
+        }
+
+
+Note:
+The value for the ID property is arbitrary, as it will be generated by our 
OData service implementation
+
+
+### 3.4.2. Example for UPDATE (PUT):
+
+  - URL: <http://localhost:8080/DemoService/DemoService.svc/Products(3)>  
+  - HTTP verb: **PUT**
+  - Header: `Content-Type: application/json; odata.metadata=minimal`  
+  - Request body:  
+
+        :::json
+        {  
+          "ID":3,  
+          "Name":"Ergo Screen updated Name",  
+          "Description":"updated description"  
+        }
+
+
+
+### 3.4.3. Example for UPDATE (PATCH):
+
+  - URL: <http://localhost:8080/DemoService/DemoService.svc/Products(3)>  
+  - HTTP verb: **PATCH**  
+  - Header: `Content-Type: application/json; odata.metadata=minimal`  
+  - Request body:  
+
+        :::json
+        {  
+          "Description": "patched description"  
+        }  
+
+
+###3.4.4. Example for DELETE:
+
+  - URL: <http://localhost:8080/DemoService/DemoService.svc/Products(3)>  
+  - HTTP verb: **DELETE**  
+  - Header: Content-Type: application/json; odata.metadata=minimal  
+  - Request body:  `<empty>`
+
+---
+
+# 4. Summary
+
+In this tutorial we have learned how to implement the creation, update and 
deletion of an entity.  
+It has been based on a simple OData model, focusing on simple sample code and 
sample data.  
+
+In the next tutorial (Part 4: Navigation) we will learn how to implement 
navigation, i.e. the linking of resources.
+
+---
+
+# 5. Links
+
+Tutorial OData V4 service part 1: [Read Entity 
Collection](/doc/odata4/tutorials/read/tutorial_read.html) | [sample project 
zip](/doc/odata4/tutorials/read/sample/DemoService_Tutorial_Read.zip)  
+Tutorial OData V4 service part 2: [Read Entity, Read 
Property](/doc/odata4/tutorials/readep/tutorial_readep.html) | [sample project 
zip](/doc/odata4/tutorials/readep/sample/DemoService_Tutorial_Readep.zip)  
+Tutorial OData V4 service part 3: [Write (Create, Update, Delete Entity) - 
(this page)](/doc/odata4/tutorials/write/tutorial_write.html) | [sample project 
zip](/doc/odata4/tutorials/write/sample/DemoService_Tutorial_Write.zip)  
+Tutorial OData V4 service, part 4: [Navigation - (To Be Announced)](...)
+
+OData specification: <http://odata.org/>
+Olingo Javadoc: <http://olingo.apache.org/javadoc/odata4/index.html>


Reply via email to