Author: buildbot
Date: Tue Nov 14 09:30:21 2017
New Revision: 1020828
Log:
Staging update by buildbot for olingo
Modified:
websites/staging/olingo/trunk/content/ (props changed)
websites/staging/olingo/trunk/content/doc/odata4/tutorials/action/tutorial_bound_action.html
Propchange: websites/staging/olingo/trunk/content/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 14 09:30:21 2017
@@ -1 +1 @@
-1815092
+1815191
Modified:
websites/staging/olingo/trunk/content/doc/odata4/tutorials/action/tutorial_bound_action.html
==============================================================================
---
websites/staging/olingo/trunk/content/doc/odata4/tutorials/action/tutorial_bound_action.html
(original)
+++
websites/staging/olingo/trunk/content/doc/odata4/tutorials/action/tutorial_bound_action.html
Tue Nov 14 09:30:21 2017
@@ -110,6 +110,7 @@ h2:hover > .headerlink, h3:hover > .head
<li><a href="#implementation">Implementation</a><ul>
<li><a href="#extend-the-metadata-model">Extend the Metadata model</a></li>
<li><a href="#extend-the-data-store">Extend the data store</a></li>
+<li><a
href="#extend-the-entity-collection-and-the-entity-processor-to-handle-functions">Extend
the entity collection and the entity processor to handle functions</a></li>
<li><a href="#implement-an-action-processor">Implement an action
processor</a></li>
</ul>
</li>
@@ -125,7 +126,7 @@ h2:hover > .headerlink, h3:hover > .head
</ul>
</div>
<h2 id="introduction">Introduction<a class="headerlink" href="#introduction"
title="Permanent link">¶</a></h2>
-<p>In the present tutorial, weâll implement a bound action.</p>
+<p>In the present tutorial, weâll implement a bound action and function.</p>
<p><strong>Note:</strong>
The final source code can be found in the project <a
href="https://git-wip-us.apache.org/repos/asf/olingo-odata4">git repository</a>.
A detailed description how to checkout the tutorials can be found <a
href="/doc/odata4/tutorials/prerequisites/prerequisites.html">here</a>. <br />
@@ -152,11 +153,11 @@ root.</p>
</ul>
<p>In addition an <em>Action</em> can return void that means there is no
return value. A <em>Function</em> must return a value.</p>
<p>First an <em>Operation</em> can be bound or unbound.In this tutorial we
will focus on bound operation. </p>
-<p>Bound actions support overloading (multiple actions having the same name
within the same namespace) by binding parameter type. The combination of action
name and the binding parameter type MUST be unique within a namespace.</p>
-<p>An action element MAY specify a Boolean value for the IsBound attribute.
-Actions whose IsBound attribute is false or not specified are considered
unbound. Unbound actions are invoked through an action import.
-Actions whose IsBound attribute is true are considered bound. Bound actions
are invoked by appending a segment containing the qualified action name to a
segment of the appropriate binding parameter type within the resource path.
Bound actions MUST contain at least one edm:Parameter element, and the first
parameter is the binding parameter. The binding parameter can be of any type,
and it MAY be nullable.</p>
-<p>Bound actions that return an entity or a collection of entities MAY specify
a value for the EntitySetPath attribute if determination of the entity set for
the return type is contingent on the binding parameter. The value for the
EntitySetPath attribute consists of a series of segments joined together with
forward slashes. The first segment of the entity set path MUST be the name of
the binding parameter. The remaining segments of the entity set path MUST
represent navigation segments or type casts.</p>
+<p>Bound actions and functions support overloading (multiple actions having
the same name within the same namespace) by binding parameter type. The
combination of action name and the binding parameter type MUST be unique within
a namespace.</p>
+<p>An action or a function element MAY specify a Boolean value for the IsBound
attribute.
+Actions/Functions whose IsBound attribute is false or not specified are
considered unbound. Unbound actions/functions are invoked through an action
import/function import.
+Actions/Functions whose IsBound attribute is true are considered bound. Bound
actions/functions are invoked by appending a segment containing the qualified
action name to a segment of the appropriate binding parameter type within the
resource path. Bound actions/functions MUST contain at least one edm:Parameter
element, and the first parameter is the binding parameter. The binding
parameter can be of any type, and it MAY be nullable.</p>
+<p>Bound actions/functions that return an entity or a collection of entities
MAY specify a value for the EntitySetPath attribute if determination of the
entity set for the return type is contingent on the binding parameter. The
value for the EntitySetPath attribute consists of a series of segments joined
together with forward slashes. The first segment of the entity set path MUST be
the name of the binding parameter. The remaining segments of the entity set
path MUST represent navigation segments or type casts.</p>
<p>A navigation segment names the SimpleIdentifier of the navigation property
to be traversed. A type cast segment names the QualifiedName of the entity type
that should be returned from the type cast.</p>
<p><strong>Example</strong></p>
<p>For example there can be a bound action createOrders which is bound to the
Customer entity having 2 parameters</p>
@@ -179,12 +180,27 @@ Actions whose IsBound attribute is true
</pre></div>
+<p>Similarly there can be a bound function GetOrders which is bound to the
Customer entity having 1 parameter</p>
+<p>Such a function can be expressed in the metadata document as follows</p>
+<div class="codehilite"><pre><span class="nt"><Function</span> <span
class="na">Name=</span><span class="s">"GetOrders"</span> <span
class="na">isBound=</span><span class="s">âtrueâ</span><span
class="nt">></span>
+ <span class="nt"><Parameter</span> <span class="na">Name=</span><span
class="s">"Customers"</span> <span class="na">Type=</span><span
class="s">"SampleEntities.Customer"</span> <span
class="na">Nullable=</span><span class="s">"false"</span><span
class="nt">/></span>
+ <span class="nt"><Parameter</span> <span class="na">Name=</span><span
class="s">"discountCode"</span> <span class="na">Type=</span><span
class="s">"Edm.String"</span> <span class="na">Nullable=</span><span
class="s">"false"</span><span class="nt">/></span>
+ <span class="nt"><ReturnType</span> <span class="na">Type=</span><span
class="s">"Collection(SampleEntities.Orders)"</span><span
class="nt">/></span>
+<span class="nt"></Function></span>
+</pre></div>
+
+
+<p>To call such a bound function the client issues a GET request to a URL
identifying the function. In this simple case such a call could look like
this:</p>
+<div class="codehilite"><pre> <span class="n">GET</span> <span
class="n">http</span><span class="p">:</span><span class="o">//</span><span
class="n">host</span><span class="o">/</span><span
class="n">service</span><span class="o">/</span><span
class="n">Customers</span><span class="p">(</span><span
class="s">'ALFKI'</span><span class="p">)</span><span
class="o">/</span><span class="n">SampleEntities</span><span
class="p">.</span><span class="n">GetOrders</span><span class="p">(</span><span
class="n">discountCode</span><span class="p">=</span><span
class="s">'BLACKFRIDAY'</span><span class="p">)</span>
+</pre></div>
+
+
<h2 id="preparation">Preparation<a class="headerlink" href="#preparation"
title="Permanent link">¶</a></h2>
<p>You should read the previous tutorials first to have an idea how to read
entities and entity collections. </p>
<p>As a shortcut you should checkout the prepared tutorial project in the <a
href="https://git-wip-us.apache.org/repos/asf/olingo-odata4">git repository</a>
in folder /samples/tutorials/p9_action_preparation.</p>
<p>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.</p>
<h2 id="implementation">Implementation<a class="headerlink"
href="#implementation" title="Permanent link">¶</a></h2>
-<p>We use the given data model you are familiar with. To keep things simple we
implement one bound action.</p>
+<p>We use the given data model you are familiar with. To keep things simple we
implement one bound action and one bound function.</p>
<p><strong>Bound Action that returns a collection of entities:
DiscountProducts</strong> <br />
This action takes bound parameter â<em>ParamCategory</em>â and an
additional parameter â<em>Amount</em>â. The action updates the price of all
products related to categories by applying the discount amount.</p>
<p>After finishing the implementation the definition of the action should be
like this:</p>
@@ -208,6 +224,28 @@ After finishing the implementation the d
<p>While actions are called by using HTTP Method POST is nessesary to
introduce new processor interfaces for actions. So there exists a bunch of
interfaces, for each return type strictly one.</p>
+<p><strong>Bound Function that returns a collection of entities:
GetDiscountedProducts</strong> <br />
+This function takes bound parameter â<em>ParamCategory</em>â and an
additional parameter â<em>Amount</em>â. The function lists all the products
related to categories which are eligible for the discount amount.</p>
+<p>After finishing the implementation the definition of the action should be
like this:</p>
+<div class="codehilite"><pre><span class="nt"><Function</span> <span
class="na">Name=</span><span class="s">"GetDiscountedProducts"</span>
<span class="na">IsBound=</span><span class="s">"true"</span><span
class="nt">></span>
+ <span class="nt"><Parameter</span> <span class="na">Name=</span><span
class="s">"ParamCategory"</span> <span class="na">Type=</span><span
class="s">"Collection(OData.Demo.Category)"</span><span
class="nt">/></span>
+ <span class="nt"><Parameter</span> <span class="na">Name=</span><span
class="s">"Amount"</span> <span class="na">Type=</span><span
class="s">"Edm.Int32"</span><span class="nt">/></span>
+ <span class="nt"><ReturnType</span> <span class="na">Type=</span><span
class="s">"Collection(OData.Demo.Product)"</span><span
class="nt">/></span>
+<span class="nt"></Function></span>
+</pre></div>
+
+
+<p><strong>Bound Function that returns an entity:
GetDiscountedProduct</strong></p>
+<p>This function takes bound parameter â<em>ParamCategory</em>â and an
additional parameter â<em>Amount</em>â. The function lists one specific
product related to a category which is eligible for the discount amount.
+After finishing the implementation the definition of the action should be like
this:</p>
+<div class="codehilite"><pre><span class="nt"><Function</span> <span
class="na">Name=</span><span class="s">"GetDiscountedProduct"</span>
<span class="na">IsBound=</span><span class="s">"true"</span><span
class="nt">></span>
+ <span class="nt"><Parameter</span> <span class="na">Name=</span><span
class="s">"ParamCategory"</span> <span class="na">Type=</span><span
class="s">âOData.Demo.Category"/</span><span class="nt">></span>
+ <span class="nt"><Parameter</span> <span class="na">Name=</span><span
class="s">"Amount"</span> <span class="na">Type=</span><span
class="s">"Edm.Int32"</span><span class="nt">/></span>
+ <span class="nt"><ReturnType</span> <span class="na">Type=</span><span
class="s">" OData.Demo.Product"</span><span class="nt">/></span>
+<span class="nt"></Function></span>
+</pre></div>
+
+
<p><strong>Steps</strong> </p>
<ul>
<li>Extend the Metadata model</li>
@@ -220,14 +258,20 @@ After finishing the implementation the d
<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">String</span> <span
class="n">ACTION_PROVIDE_DISCOUNT</span> <span class="o">=</span> <span
class="s">"DiscountProducts"</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">FullQualifiedName</span> <span
class="n">ACTION_PROVIDE_DISCOUNT_FQN</span> <span class="o">=</span> <span
class="k">new</span> <span class="n">FullQualifiedName</span><span
class="o">(</span><span class="n">NAMESPACE</span><span class="o">,</span>
<span class="n">ACTION_PROVIDE_DISCOUNT</span><span class="o">);</span>
-<span class="c1">//Bound Action</span>
<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">String</span> <span
class="n">ACTION_PROVIDE_DISCOUNT_FOR_PRODUCT</span> <span class="o">=</span>
<span class="s">"DiscountProduct"</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">FullQualifiedName</span> <span
class="n">ACTION_PROVIDE_DISCOUNT_FOR_PRODUCT_FQN</span> <span
class="o">=</span> <span class="k">new</span> <span
class="n">FullQualifiedName</span><span class="o">(</span><span
class="n">NAMESPACE</span><span class="o">,</span> <span
class="n">ACTION_PROVIDE_DISCOUNT_FOR_PRODUCT</span><span class="o">);</span>
-<span class="c1">//Action Parameters</span>
+<span class="c1">//Bound Function</span>
+<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">String</span> <span
class="n">FUNCTION_PROVIDE_DISCOUNT</span> <span class="o">=</span> <span
class="s">"GetDiscountedProducts"</span><span class="o">;</span>
+<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">FullQualifiedName</span> <span
class="n">FUNCTION_PROVIDE_DISCOUNT_FQN</span> <span class="o">=</span> <span
class="k">new</span> <span class="n">FullQualifiedName</span><span
class="o">(</span><span class="n">NAMESPACE</span><span class="o">,</span>
<span class="n">FUNCTION_PROVIDE_DISCOUNT</span><span class="o">);</span>
+
+<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">String</span> <span
class="n">FUNCTION_PROVIDE_DISCOUNT_FOR_PRODUCT</span> <span class="o">=</span>
<span class="s">"GetDiscountedProduct"</span><span class="o">;</span>
+<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">FullQualifiedName</span> <span
class="n">FUNCTION_PROVIDE_DISCOUNT_FOR_PRODUCT_FQN</span> <span
class="o">=</span> <span class="k">new</span> <span
class="n">FullQualifiedName</span><span class="o">(</span><span
class="n">NAMESPACE</span><span class="o">,</span> <span
class="n">FUNCTION_PROVIDE_DISCOUNT_FOR_PRODUCT</span><span class="o">);</span>
+
+<span class="c1">//Parameters</span>
<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">String</span> <span
class="n">PARAMETER_AMOUNT</span> <span class="o">=</span> <span
class="s">"Amount"</span><span class="o">;</span>
-<span class="c1">//Bound Action Binding Parameter</span>
+<span class="c1">//Binding Parameter</span>
<span class="kd">public</span> <span class="kd">static</span> <span
class="kd">final</span> <span class="n">String</span> <span
class="n">PARAMETER_CATEGORY</span> <span class="o">=</span> <span
class="s">"ParamCategory"</span><span class="o">;</span>
</pre></div>
@@ -235,6 +279,7 @@ After finishing the implementation the d
<p>The way to announce the operations is very similar to announcing
EntityTypes. We have to override some methods. Those methods provide the
definition of the Edm elements. We need methods for:</p>
<ul>
<li>Actions</li>
+<li>Functions</li>
</ul>
<p>The code is simple and straight forward. We need to create a list of
parameters of which the first parameter should be the binding parameter, then
create a return type. At the end all parts are fit together and get returned as
new CsdlAction Object.</p>
<div class="codehilite"><pre> <span class="nd">@Override</span>
@@ -293,12 +338,75 @@ After finishing the implementation the d
</pre></div>
+<p>Similarly, for functions we need to create a list of parameters of which
the first parameter should be the binding parameter, then create a return type.
At the end all parts are fit together and get returned as new CsdlFunction
Object.</p>
+<div class="codehilite"><pre> <span class="nd">@Override</span>
+ <span class="kd">public</span> <span class="n">List</span><span
class="o"><</span><span class="n">CsdlFunction</span><span
class="o">></span> <span class="n">getFunctions</span><span
class="o">(</span><span class="kd">final</span> <span
class="n">FullQualifiedName</span> <span class="n">functionName</span><span
class="o">)</span> <span class="o">{</span>
+ <span class="c1">// It is allowed to overload functions, so we have to
provide a list of Functions for each function name</span>
+ <span class="kd">final</span> <span class="n">List</span><span
class="o"><</span><span class="n">CsdlFunction</span><span
class="o">></span> <span class="n">functions</span> <span class="o">=</span>
<span class="k">new</span> <span class="n">ArrayList</span><span
class="o"><</span><span class="n">CsdlFunction</span><span
class="o">>();</span>
+
+ <span class="k">if</span> <span class="o">(</span><span
class="n">functionName</span><span class="o">.</span><span
class="na">equals</span><span class="o">(</span><span
class="n">FUNCTION_PROVIDE_DISCOUNT_FQN</span><span class="o">))</span> <span
class="o">{</span>
+ <span class="c1">// Create parameters</span>
+ <span class="kd">final</span> <span class="n">List</span><span
class="o"><</span><span class="n">CsdlParameter</span><span
class="o">></span> <span class="n">parameters</span> <span
class="o">=</span> <span class="k">new</span> <span
class="n">ArrayList</span><span class="o"><</span><span
class="n">CsdlParameter</span><span class="o">>();</span>
+ <span class="n">CsdlParameter</span> <span class="n">parameter</span>
<span class="o">=</span> <span class="k">new</span> <span
class="n">CsdlParameter</span><span class="o">();</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setName</span><span class="o">(</span><span
class="n">PARAMETER_CATEGORY</span><span class="o">);</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setType</span><span class="o">(</span><span
class="n">ET_CATEGORY_FQN</span><span class="o">);</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setCollection</span><span class="o">(</span><span
class="kc">true</span><span class="o">);</span>
+ <span class="n">parameters</span><span class="o">.</span><span
class="na">add</span><span class="o">(</span><span
class="n">parameter</span><span class="o">);</span>
+ <span class="n">parameter</span> <span class="o">=</span> <span
class="k">new</span> <span class="n">CsdlParameter</span><span
class="o">();</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setName</span><span class="o">(</span><span
class="n">PARAMETER_AMOUNT</span><span class="o">);</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setType</span><span class="o">(</span><span
class="n">EdmPrimitiveTypeKind</span><span class="o">.</span><span
class="na">Int32</span><span class="o">.</span><span
class="na">getFullQualifiedName</span><span class="o">());</span>
+ <span class="n">parameters</span><span class="o">.</span><span
class="na">add</span><span class="o">(</span><span
class="n">parameter</span><span class="o">);</span>
+
+ <span class="c1">// Create the Csdl Function</span>
+ <span class="kd">final</span> <span class="n">CsdlFunction</span> <span
class="n">function</span> <span class="o">=</span> <span class="k">new</span>
<span class="n">CsdlFunction</span><span class="o">();</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setName</span><span class="o">(</span><span
class="n">FUNCTION_PROVIDE_DISCOUNT_FQN</span><span class="o">.</span><span
class="na">getName</span><span class="o">());</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setBound</span><span class="o">(</span><span
class="kc">true</span><span class="o">);</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setParameters</span><span class="o">(</span><span
class="n">parameters</span><span class="o">);</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setReturnType</span><span class="o">(</span><span
class="k">new</span> <span class="n">CsdlReturnType</span><span
class="o">().</span><span class="na">setType</span><span
class="o">(</span><span class="n">ET_PRODUCT_FQN</span><span
class="o">).</span><span class="na">setCollection</span><span
class="o">(</span><span class="kc">true</span><span class="o">));</span>
+ <span class="n">functions</span><span class="o">.</span><span
class="na">add</span><span class="o">(</span><span
class="n">function</span><span class="o">);</span>
+
+ <span class="k">return</span> <span class="n">functions</span><span
class="o">;</span>
+ <span class="o">}</span> <span class="k">else</span> <span
class="k">if</span> <span class="o">(</span><span
class="n">functionName</span><span class="o">.</span><span
class="na">equals</span><span class="o">(</span><span
class="n">FUNCTION_PROVIDE_DISCOUNT_FOR_PRODUCT_FQN</span><span
class="o">))</span> <span class="o">{</span>
+ <span class="c1">// Create parameters</span>
+ <span class="kd">final</span> <span class="n">List</span><span
class="o"><</span><span class="n">CsdlParameter</span><span
class="o">></span> <span class="n">parameters</span> <span
class="o">=</span> <span class="k">new</span> <span
class="n">ArrayList</span><span class="o"><</span><span
class="n">CsdlParameter</span><span class="o">>();</span>
+ <span class="n">CsdlParameter</span> <span class="n">parameter</span>
<span class="o">=</span> <span class="k">new</span> <span
class="n">CsdlParameter</span><span class="o">();</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setName</span><span class="o">(</span><span
class="n">PARAMETER_CATEGORY</span><span class="o">);</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setType</span><span class="o">(</span><span
class="n">ET_CATEGORY_FQN</span><span class="o">);</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setCollection</span><span class="o">(</span><span
class="kc">false</span><span class="o">);</span>
+ <span class="n">parameters</span><span class="o">.</span><span
class="na">add</span><span class="o">(</span><span
class="n">parameter</span><span class="o">);</span>
+ <span class="n">parameter</span> <span class="o">=</span> <span
class="k">new</span> <span class="n">CsdlParameter</span><span
class="o">();</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setName</span><span class="o">(</span><span
class="n">PARAMETER_AMOUNT</span><span class="o">);</span>
+ <span class="n">parameter</span><span class="o">.</span><span
class="na">setType</span><span class="o">(</span><span
class="n">EdmPrimitiveTypeKind</span><span class="o">.</span><span
class="na">Int32</span><span class="o">.</span><span
class="na">getFullQualifiedName</span><span class="o">());</span>
+ <span class="n">parameters</span><span class="o">.</span><span
class="na">add</span><span class="o">(</span><span
class="n">parameter</span><span class="o">);</span>
+
+ <span class="c1">// Create the Csdl Function</span>
+ <span class="kd">final</span> <span class="n">CsdlFunction</span> <span
class="n">function</span><span class="o">=</span> <span class="k">new</span>
<span class="n">CsdlFunction</span><span class="o">();</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setName</span><span class="o">(</span><span
class="n">ACTION_PROVIDE_DISCOUNT_FOR_PRODUCT_FQN</span><span
class="o">.</span><span class="na">getName</span><span class="o">());</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setBound</span><span class="o">(</span><span
class="kc">true</span><span class="o">);</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setParameters</span><span class="o">(</span><span
class="n">parameters</span><span class="o">);</span>
+ <span class="n">function</span><span class="o">.</span><span
class="na">setReturnType</span><span class="o">(</span><span
class="k">new</span> <span class="n">CsdlReturnType</span><span
class="o">().</span><span class="na">setType</span><span
class="o">(</span><span class="n">ET_PRODUCT_FQN</span><span
class="o">).</span><span class="na">setCollection</span><span
class="o">(</span><span class="kc">false</span><span class="o">));</span>
+ <span class="n">functions</span><span class="o">.</span><span
class="na">add</span><span class="o">(</span><span
class="n">function</span><span class="o">);</span>
+
+ <span class="k">return</span> <span class="n">functions</span><span
class="o">;</span>
+ <span class="o">}</span>
+
+ <span class="k">return</span> <span class="kc">null</span><span
class="o">;</span>
+ <span class="o">}</span>
+</pre></div>
+
+
<p>Finally we have to announce these operations to the schema. Add the
following lines to the method getSchemas():</p>
<div class="codehilite"><pre><span class="c1">// add actions</span>
<span class="n">List</span><span class="o"><</span><span
class="n">CsdlAction</span><span class="o">></span> <span
class="n">actions</span> <span class="o">=</span> <span class="k">new</span>
<span class="n">ArrayList</span><span class="o"><</span><span
class="n">CsdlAction</span><span class="o">>();</span>
<span class="n">actions</span><span class="o">.</span><span
class="na">addAll</span><span class="o">(</span><span
class="n">getActions</span><span class="o">(</span><span
class="n">ACTION_PROVIDE_DISCOUNT_FQN</span><span class="o">));</span>
<span class="n">actions</span><span class="o">.</span><span
class="na">addAll</span><span class="o">(</span><span
class="n">getActions</span><span class="o">(</span><span
class="n">ACTION_PROVIDE_DISCOUNT_FOR_PRODUCT_FQN</span><span
class="o">));</span>
<span class="n">schema</span><span class="o">.</span><span
class="na">setActions</span><span class="o">(</span><span
class="n">actions</span><span class="o">);</span>
+
+<span class="c1">// add functions</span>
+<span class="n">List</span><span class="o"><</span><span
class="n">CsdlFunction</span><span class="o">></span> <span
class="n">functions</span> <span class="o">=</span> <span class="k">new</span>
<span class="n">ArrayList</span><span class="o"><</span><span
class="n">CsdlFunction</span><span class="o">>();</span>
+<span class="n">functions</span><span class="o">.</span><span
class="na">addAll</span><span class="o">(</span><span
class="n">getFunctions</span><span class="o">(</span><span
class="n">FUNCTION_PROVIDE_DISCOUNT_FQN</span><span class="o">));</span>
+<span class="n">functions</span><span class="o">.</span><span
class="na">addAll</span><span class="o">(</span><span
class="n">getFunctions</span><span class="o">(</span><span
class="n">FUNCTION_PROVIDE_DISCOUNT_FOR_PRODUCT_FQN</span><span
class="o">));</span>
+<span class="n">schema</span><span class="o">.</span><span
class="na">setFunctions</span><span class="o">(</span><span
class="n">functions</span><span class="o">);</span>
</pre></div>
@@ -363,6 +471,208 @@ After finishing the implementation the d
</pre></div>
+<p>We also create methods for GetDiscountedProducts and GetDiscountedProduct
functions. </p>
+<div class="codehilite"><pre> <span class="kd">public</span> <span
class="n">EntityCollection</span> <span
class="nf">getBoundFunctionEntityCollection</span><span class="o">(</span><span
class="n">EdmFunction</span> <span class="n">function</span><span
class="o">,</span> <span class="n">Integer</span> <span
class="n">amount</span><span class="o">)</span> <span class="o">{</span>
+ <span class="n">EntityCollection</span> <span class="n">collection</span>
<span class="o">=</span> <span class="k">new</span> <span
class="n">EntityCollection</span><span class="o">();</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="s">"GetDiscountedProducts"</span><span class="o">.</span><span
class="na">equals</span><span class="o">(</span><span
class="n">function</span><span class="o">.</span><span
class="na">getName</span><span class="o">()))</span> <span class="o">{</span>
+ <span class="k">for</span> <span class="o">(</span><span
class="n">Entity</span> <span class="n">entity</span> <span class="o">:</span>
<span class="n">categoryList</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="k">if</span><span class="o">(</span><span
class="n">amount</span> <span class="o">>=</span> <span
class="n">entity</span><span class="o">.</span><span
class="na">getProperty</span><span class="o">(</span><span
class="s">"amount"</span><span class="o">)){</span>
+ <span class="n">Entity</span> <span class="n">en</span> <span
class="o">=</span> <span class="n">getRelatedEntity</span><span
class="o">(</span><span class="n">entity</span><span class="o">,</span> <span
class="o">(</span><span class="n">EdmEntityType</span><span class="o">)</span>
<span class="n">function</span><span class="o">.</span><span
class="na">getReturnType</span><span class="o">().</span><span
class="na">getType</span><span class="o">());</span>
+ <span class="n">collection</span><span class="o">.</span><span
class="na">getEntities</span><span class="o">().</span><span
class="na">add</span><span class="o">(</span><span class="n">en</span><span
class="o">);</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+ <span class="k">return</span> <span class="n">collection</span><span
class="o">;</span>
+ <span class="o">}</span>
+
+ <span class="kd">public</span> <span class="n">Entity</span> <span
class="nf">getBoundFunctionEntity</span><span class="o">(</span><span
class="n">EdmAction</span> <span class="n">function</span><span
class="o">,</span> <span class="n">Integer</span> <span
class="n">amount</span><span class="o">,</span>
+ <span class="n">List</span><span class="o"><</span><span
class="n">UriParameter</span><span class="o">></span> <span
class="n">keyParams</span><span class="o">)</span> <span
class="kd">throws</span> <span class="n">ODataApplicationException</span> <span
class="o">{</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="s">"GetDiscountedProduct"</span><span class="o">.</span><span
class="na">equals</span><span class="o">(</span><span
class="n">function</span><span class="o">.</span><span
class="na">getName</span><span class="o">()))</span> <span class="o">{</span>
+ <span class="k">for</span> <span class="o">(</span><span
class="n">Entity</span> <span class="n">entity</span> <span class="o">:</span>
<span class="n">categoryList</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="k">if</span><span class="o">(</span><span
class="n">amount</span><span class="o">==</span> <span
class="n">entity</span><span class="o">.</span><span
class="na">getProperty</span><span class="o">(</span><span
class="s">"amount"</span><span class="o">)){</span>
+ <span class="k">return</span> <span
class="nf">getRelatedEntity</span><span class="o">(</span><span
class="n">entity</span><span class="o">,</span> <span class="o">(</span><span
class="n">EdmEntityType</span><span class="o">)</span> <span
class="n">function</span><span class="o">.</span><span
class="na">getReturnType</span><span class="o">().</span><span
class="na">getType</span><span class="o">(),</span> <span
class="n">keyParams</span><span class="o">);</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+ <span class="k">return</span> <span class="kc">null</span><span
class="o">;</span>
+ <span class="o">}</span>
+</pre></div>
+
+
+<h3
id="extend-the-entity-collection-and-the-entity-processor-to-handle-functions">Extend
the entity collection and the entity processor to handle functions<a
class="headerlink"
href="#extend-the-entity-collection-and-the-entity-processor-to-handle-functions"
title="Permanent link">¶</a></h3>
+<p>We start with the entity collection processor
DemoEntityCollectionProcessor. To keep things simple, the first steps is to
distinguish between entity collections and function imports. A cleverer
implementation can handle both cases in one method to avoid duplicated code.</p>
+<p>The recent implementation of the readEntityCollection() has been moved to
readEntityCollectionInternal()</p>
+<p>::::java
+public void readEntityCollection(ODataRequest request, ODataResponse response,
UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException,
SerializerException {</p>
+<p>final UriResource firstResourceSegment =
uriInfo.getUriResourceParts().get(0);</p>
+<p>if(firstResourceSegment instanceof UriResourceEntitySet) {
+ readEntityCollectionInternal(request, response, uriInfo, responseFormat);
+ } else if(firstResourceSegment instanceof UriResourceFunction) {
+ readFunctionImportCollection(request, response, uriInfo, responseFormat);
+ } else {
+ throw new ODataApplicationException("Not implemented",
+ HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),
+ Locale.ENGLISH);
+ }
+}
+Like by reading entity collections, the first step is to analyze the URI and
then fetch the data (of the function).</p>
+<div class="codehilite"><pre> <span class="kd">private</span> <span
class="kt">void</span> <span
class="nf">readEntityCollectionInternal</span><span class="o">(</span><span
class="kd">final</span> <span class="n">ODataRequest</span> <span
class="n">request</span><span class="o">,</span> <span class="kd">final</span>
<span class="n">ODataResponse</span> <span class="n">response</span><span
class="o">,</span>
+ <span class="kd">final</span> <span class="n">UriInfo</span> <span
class="n">uriInfo</span><span class="o">,</span> <span class="kd">final</span>
<span class="n">ContentType</span> <span class="n">responseFormat</span><span
class="o">)</span> <span class="kd">throws</span> <span
class="n">ODataApplicationException</span><span class="o">,</span> <span
class="n">SerializerException</span> <span class="o">{</span>
+
+ <span class="n">EdmEntitySet</span> <span
class="n">responseEdmEntitySet</span> <span class="o">=</span> <span
class="kc">null</span><span class="o">;</span> <span class="c1">// we'll
need this to build the ContextURL</span>
+ <span class="n">EntityCollection</span> <span
class="n">responseEntityCollection</span> <span class="o">=</span> <span
class="kc">null</span><span class="o">;</span> <span class="c1">// we'll
need this to set the response body</span>
+
+ <span class="c1">// 1st retrieve the requested EntitySet from the uriInfo
(representation of the parsed URI)</span>
+ <span class="n">List</span><span class="o"><</span><span
class="n">UriResource</span><span class="o">></span> <span
class="n">resourceParts</span> <span class="o">=</span> <span
class="n">uriInfo</span><span class="o">.</span><span
class="na">getUriResourceParts</span><span class="o">();</span>
+ <span class="kt">int</span> <span class="n">segmentCount</span> <span
class="o">=</span> <span class="n">resourceParts</span><span
class="o">.</span><span class="na">size</span><span class="o">();</span>
+
+ <span class="n">UriResource</span> <span class="n">uriResource</span> <span
class="o">=</span> <span class="n">resourceParts</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">0</span><span class="o">);</span> <span class="c1">// in our
example, the first segment is the EntitySet</span>
+ <span class="k">if</span> <span class="o">(!(</span><span
class="n">uriResource</span> <span class="k">instanceof</span> <span
class="n">UriResourceEntitySet</span><span class="o">))</span> <span
class="o">{</span>
+ <span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Only EntitySet is supported"</span><span class="o">,</span>
+ <span class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">NOT_IMPLEMENTED</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">(),</span> <span
class="n">Locale</span><span class="o">.</span><span
class="na">ROOT</span><span class="o">);</span>
+ <span class="o">}</span>
+
+ <span class="n">UriResourceEntitySet</span> <span
class="n">uriResourceEntitySet</span> <span class="o">=</span> <span
class="o">(</span><span class="n">UriResourceEntitySet</span><span
class="o">)</span> <span class="n">uriResource</span><span class="o">;</span>
+ <span class="n">EdmEntitySet</span> <span class="n">startEdmEntitySet</span>
<span class="o">=</span> <span class="n">uriResourceEntitySet</span><span
class="o">.</span><span class="na">getEntitySet</span><span class="o">();</span>
+
+ <span class="k">if</span> <span class="o">(</span><span
class="n">segmentCount</span> <span class="o">==</span> <span
class="mi">1</span><span class="o">)</span> <span class="o">{</span>
+ <span class="c1">// This is a normal query fetch the entity from backend and
return entityset</span>
+ <span class="o">}</span>
+
+ <span class="k">else</span> <span class="nf">if</span> <span
class="o">(</span><span class="n">segmentCount</span> <span class="o">==</span>
<span class="mi">2</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// in case of function or navigation</span>
+
+ <span class="n">UriResource</span> <span class="n">lastSegment</span>
<span class="o">=</span> <span class="n">resourceParts</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">1</span><span class="o">);</span> <span class="c1">// in our example
we don't support more complex URIs</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="n">lastSegment</span> <span class="k">instanceof</span> <span
class="n">UriResourceFunction</span><span class="o">)</span> <span
class="o">{</span><span class="c1">// For bound function</span>
+ <span class="n">UriResourceFunction</span> <span
class="n">uriResourceFunction</span> <span class="o">=</span> <span
class="o">(</span><span class="n">UriResourceFunction</span><span
class="o">)</span> <span class="n">lastSegment</span><span class="o">;</span>
+ <span class="c1">// 2nd: fetch the data from backend</span>
+ <span class="c1">// first fetch the target entity type </span>
+ <span class="n">String</span> <span class="n">targetEntityType</span>
<span class="o">=</span> <span class="n">uriResourceFunction</span><span
class="o">.</span><span class="na">getFunction</span><span
class="o">().</span><span class="na">getReturnType</span><span
class="o">().</span><span class="na">getType</span><span
class="o">().</span><span class="na">getName</span><span class="o">();</span>
+ <span class="c1">// contextURL displays the last segment</span>
+ <span class="k">for</span><span class="o">(</span><span
class="n">EdmEntitySet</span> <span class="n">entitySet</span> <span
class="o">:</span> <span class="n">serviceMetadata</span><span
class="o">.</span><span class="na">getEdm</span><span class="o">().</span><span
class="na">getEntityContainer</span><span class="o">().</span><span
class="na">getEntitySets</span><span class="o">()){</span>
+ <span class="k">if</span><span class="o">(</span><span
class="n">targetEntityType</span><span class="o">.</span><span
class="na">equals</span><span class="o">(</span><span
class="n">entitySet</span><span class="o">.</span><span
class="na">getEntityType</span><span class="o">().</span><span
class="na">getName</span><span class="o">())){</span>
+ <span class="n">responseEdmEntitySet</span> <span class="o">=</span>
<span class="n">entitySet</span><span class="o">;</span>
+ <span class="k">break</span><span class="o">;</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+
+ <span class="c1">// error handling for null entities</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="n">targetEntityType</span> <span class="o">==</span> <span
class="kc">null</span> <span class="o">||</span> <span
class="n">responseEdmEntitySet</span> <span class="o">==</span> <span
class="kc">null</span><span class="o">)</span> <span class="o">{</span>
+ <span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Entity not found."</span><span class="o">,</span>
+ <span class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">NOT_FOUND</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">(),</span> <span
class="n">Locale</span><span class="o">.</span><span
class="na">ROOT</span><span class="o">);</span>
+ <span class="o">}</span>
+ <span class="n">Integer</span> <span class="n">amount</span> <span
class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span
class="na">parseInt</span><span class="o">(</span><span
class="n">uriResourceFunction</span><span class="o">.</span><span
class="na">getParameters</span><span class="o">().</span><span
class="na">get</span><span class="o">(</span><span class="mi">0</span><span
class="o">).</span><span class="na">getText</span><span class="o">())</span>
+ <span class="c1">// then fetch the entity collection for the target
type</span>
+ <span class="n">responseEntityCollection</span> <span class="o">=</span>
<span class="n">storage</span><span class="o">.</span><span
class="na">getBoundFunctionEntityCollection</span><span class="o">(</span><span
class="n">function</span><span class="o">,</span> <span
class="n">amount</span><span class="o">);</span>
+ <span class="o">}</span>
+</pre></div>
+
+
+<p>}</p>
+<p>Then the result has to be serialized. The only difference to entity sets is
the way how the EdmEntityType is determined.</p>
+<div class="codehilite"><pre> <span class="p">::::</span><span
class="n">java</span>
+ <span class="o">//</span> 3<span class="n">rd</span><span class="p">:</span>
<span class="n">create</span> <span class="n">and</span> <span
class="n">configure</span> <span class="n">a</span> <span
class="n">serializer</span>
+<span class="n">ContextURL</span> <span class="n">contextUrl</span> <span
class="p">=</span> <span class="n">ContextURL</span><span
class="p">.</span><span class="n">with</span><span class="p">().</span><span
class="n">entitySet</span><span class="p">(</span><span
class="n">responseEdmEntitySet</span><span class="p">).</span><span
class="n">build</span><span class="p">();</span>
+<span class="n">final</span> <span class="n">String</span> <span
class="n">id</span> <span class="p">=</span> <span
class="n">request</span><span class="p">.</span><span
class="n">getRawBaseUri</span><span class="p">()</span> <span
class="o">+</span> "<span class="o">/</span>" <span
class="o">+</span> <span class="n">responseEdmEntitySet</span><span
class="p">.</span><span class="n">getName</span><span class="p">();</span>
+<span class="n">EntityCollectionSerializerOptions</span> <span
class="n">opts</span> <span class="p">=</span> <span
class="n">EntityCollectionSerializerOptions</span><span class="p">.</span><span
class="n">with</span><span class="p">()</span>
+ <span class="p">.</span><span class="n">contextURL</span><span
class="p">(</span><span class="n">contextUrl</span><span
class="p">).</span><span class="n">id</span><span class="p">(</span><span
class="n">id</span><span class="p">).</span><span class="n">build</span><span
class="p">();</span>
+<span class="n">EdmEntityType</span> <span class="n">edmEntityType</span>
<span class="p">=</span> <span class="n">responseEdmEntitySet</span><span
class="p">.</span><span class="n">getEntityType</span><span class="p">();</span>
+
+<span class="n">ODataSerializer</span> <span class="n">serializer</span> <span
class="p">=</span> <span class="n">odata</span><span class="p">.</span><span
class="n">createSerializer</span><span class="p">(</span><span
class="n">responseFormat</span><span class="p">);</span>
+<span class="n">SerializerResult</span> <span
class="n">serializerResult</span> <span class="p">=</span> <span
class="n">serializer</span><span class="p">.</span><span
class="n">entityCollection</span><span class="p">(</span><span
class="n">serviceMetadata</span><span class="p">,</span> <span
class="n">edmEntityType</span><span class="p">,</span>
+ <span class="n">responseEntityCollection</span><span class="p">,</span>
<span class="n">opts</span><span class="p">);</span>
+
+<span class="o">//</span> 4<span class="n">th</span><span class="p">:</span>
<span class="n">configure</span> <span class="n">the</span> <span
class="n">response</span> <span class="n">object</span><span class="p">:</span>
<span class="n">set</span> <span class="n">the</span> <span
class="n">body</span><span class="p">,</span> <span class="n">headers</span>
<span class="n">and</span> <span class="n">status</span> <span
class="n">code</span>
+<span class="n">response</span><span class="p">.</span><span
class="n">setContent</span><span class="p">(</span><span
class="n">serializerResult</span><span class="p">.</span><span
class="n">getContent</span><span class="p">());</span>
+<span class="n">response</span><span class="p">.</span><span
class="n">setStatusCode</span><span class="p">(</span><span
class="n">HttpStatusCode</span><span class="p">.</span><span
class="n">OK</span><span class="p">.</span><span
class="n">getStatusCode</span><span class="p">());</span>
+<span class="n">response</span><span class="p">.</span><span
class="n">setHeader</span><span class="p">(</span><span
class="n">HttpHeader</span><span class="p">.</span><span
class="n">CONTENT_TYPE</span><span class="p">,</span> <span
class="n">responseFormat</span><span class="p">.</span><span
class="n">toContentTypeString</span><span class="p">());</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Next we will implement the processor to read a single entity. The
implementation is quite similar to the implementation of the collection
processor.</p>
+<div class="codehilite"><pre><span class="kd">public</span> <span
class="kt">void</span> <span class="nf">readEntity</span><span
class="o">(</span><span class="n">ODataRequest</span> <span
class="n">request</span><span class="o">,</span> <span
class="n">ODataResponse</span> <span class="n">response</span><span
class="o">,</span> <span class="n">UriInfo</span> <span
class="n">uriInfo</span><span class="o">,</span> <span
class="n">ContentType</span> <span class="n">responseFormat</span><span
class="o">)</span>
+ <span class="kd">throws</span> <span
class="n">ODataApplicationException</span><span class="o">,</span> <span
class="n">SerializerException</span> <span class="o">{</span>
+
+ <span class="n">UriResource</span> <span class="n">uriResource</span> <span
class="o">=</span> <span class="n">uriInfo</span><span class="o">.</span><span
class="na">getUriResourceParts</span><span class="o">().</span><span
class="na">get</span><span class="o">(</span><span class="mi">0</span><span
class="o">);</span>
+
+ <span class="k">if</span><span class="o">(</span><span
class="n">uriResource</span> <span class="k">instanceof</span> <span
class="n">UriResourceEntitySet</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="n">readEntityInternal</span><span class="o">(</span><span
class="n">request</span><span class="o">,</span> <span
class="n">response</span><span class="o">,</span> <span
class="n">uriInfo</span><span class="o">,</span> <span
class="n">responseFormat</span><span class="o">);</span>
+ <span class="o">}</span> <span class="k">else</span> <span
class="k">if</span><span class="o">(</span><span class="n">uriResource</span>
<span class="k">instanceof</span> <span
class="n">UriResourceFunction</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="n">readFunctionImportInternal</span><span
class="o">(</span><span class="n">request</span><span class="o">,</span> <span
class="n">response</span><span class="o">,</span> <span
class="n">uriInfo</span><span class="o">,</span> <span
class="n">responseFormat</span><span class="o">);</span>
+ <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
+ <span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Only EntitySet is supported"</span><span class="o">,</span>
+ <span class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">NOT</span><span class="o">&</span><span
class="err">#</span><span class="mi">95</span><span class="o">;</span><span
class="n">IMPLEMENTED</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">(),</span> <span
class="n">Locale</span><span class="o">.</span><span
class="na">ENGLISH</span><span class="o">);</span>
+ <span class="o">}</span>
+<span class="o">}</span>
+
+ <span class="kd">private</span> <span class="kt">void</span> <span
class="nf">readEntityInternal</span><span class="o">(</span><span
class="kd">final</span> <span class="n">ODataRequest</span> <span
class="n">request</span><span class="o">,</span> <span class="kd">final</span>
<span class="n">ODataResponse</span> <span class="n">response</span><span
class="o">,</span>
+ <span class="kd">final</span> <span class="n">UriInfo</span> <span
class="n">uriInfo</span><span class="o">,</span> <span class="kd">final</span>
<span class="n">ContentType</span> <span class="n">responseFormat</span><span
class="o">)</span> <span class="kd">throws</span> <span
class="n">ODataApplicationException</span><span class="o">,</span> <span
class="n">SerializerException</span> <span class="o">{</span>
+
+ <span class="n">EdmEntityType</span> <span
class="n">responseEdmEntityType</span> <span class="o">=</span> <span
class="kc">null</span><span class="o">;</span> <span class="c1">// we'll
need this to build the ContextURL</span>
+ <span class="n">Entity</span> <span class="n">responseEntity</span> <span
class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span
class="c1">// required for serialization of the response body</span>
+ <span class="n">EdmEntitySet</span> <span
class="n">responseEdmEntitySet</span> <span class="o">=</span> <span
class="kc">null</span><span class="o">;</span> <span class="c1">// we need this
for building the contextUrl</span>
+
+ <span class="c1">// 1st step: retrieve the requested Entity: can be
"normal" read operation, or navigation (to-one)</span>
+ <span class="n">List</span><span class="o"><</span><span
class="n">UriResource</span><span class="o">></span> <span
class="n">resourceParts</span> <span class="o">=</span> <span
class="n">uriInfo</span><span class="o">.</span><span
class="na">getUriResourceParts</span><span class="o">();</span>
+ <span class="kt">int</span> <span class="n">segmentCount</span> <span
class="o">=</span> <span class="n">resourceParts</span><span
class="o">.</span><span class="na">size</span><span class="o">();</span>
+
+ <span class="n">UriResource</span> <span class="n">uriResource</span> <span
class="o">=</span> <span class="n">resourceParts</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">0</span><span class="o">);</span> <span class="c1">// in our
example, the first segment is the EntitySet</span>
+ <span class="n">UriResourceEntitySet</span> <span
class="n">uriResourceEntitySet</span> <span class="o">=</span> <span
class="o">(</span><span class="n">UriResourceEntitySet</span><span
class="o">)</span> <span class="n">uriResource</span><span class="o">;</span>
+ <span class="n">EdmEntitySet</span> <span
class="n">startEdmEntitySet</span> <span class="o">=</span> <span
class="n">uriResourceEntitySet</span><span class="o">.</span><span
class="na">getEntitySet</span><span class="o">();</span>
+
+ <span class="k">if</span> <span class="o">(</span><span
class="n">segmentCount</span> <span class="o">==</span> <span
class="mi">1</span><span class="o">)</span> <span class="o">{</span>
+ <span class="c1">// This is a normal read call fetch the entity from backend
and return entity</span>
+ <span class="o">}</span> <span class="k">else</span> <span
class="k">if</span> <span class="o">(</span><span class="n">segmentCount</span>
<span class="o">==</span> <span class="mi">2</span><span class="o">)</span>
<span class="o">{</span> <span class="c1">// Bound Function or navigation</span>
+ <span class="n">UriResource</span> <span class="n">segment</span> <span
class="o">=</span> <span class="n">resourceParts</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">1</span><span class="o">);</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="n">segment</span> <span class="k">instanceof</span> <span
class="n">UriResourceFunction</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="n">UriResourceFunction</span> <span
class="n">uriResourceFunction</span> <span class="o">=</span> <span
class="o">(</span><span class="n">UriResourceFunction</span><span
class="o">)</span> <span class="n">segment</span><span class="o">;</span>
+
+ <span class="c1">// 2nd: fetch the data from backend.</span>
+ <span class="c1">// first fetch the target entity type </span>
+ <span class="n">String</span> <span class="n">targetEntityType</span>
<span class="o">=</span> <span class="n">uriResourceFunction</span><span
class="o">.</span><span class="na">getFunction</span><span
class="o">().</span><span class="na">getReturnType</span><span
class="o">().</span><span class="na">getType</span><span
class="o">().</span><span class="na">getName</span><span class="o">();</span>
+
+ <span class="c1">// contextURL displays the last segment</span>
+ <span class="k">for</span><span class="o">(</span><span
class="n">EdmEntitySet</span> <span class="n">entitySet</span> <span
class="o">:</span> <span class="n">serviceMetadata</span><span
class="o">.</span><span class="na">getEdm</span><span class="o">().</span><span
class="na">getEntityContainer</span><span class="o">().</span><span
class="na">getEntitySets</span><span class="o">()){</span>
+ <span class="k">if</span><span class="o">(</span><span
class="n">targetEntityType</span><span class="o">.</span><span
class="na">equals</span><span class="o">(</span><span
class="n">entitySet</span><span class="o">.</span><span
class="na">getEntityType</span><span class="o">().</span><span
class="na">getName</span><span class="o">())){</span>
+ <span class="n">responseEdmEntityType</span> <span
class="o">=</span> <span class="n">entitySet</span><span
class="o">.</span><span class="na">getEntityType</span><span
class="o">();</span>
+ <span class="n">responseEdmEntitySet</span> <span class="o">=</span>
<span class="n">entitySet</span><span class="o">;</span>
+ <span class="k">break</span><span class="o">;</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+
+ <span class="c1">// error handling for null entities</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="n">targetEntityType</span> <span class="o">==</span> <span
class="kc">null</span> <span class="o">||</span> <span
class="n">responseEdmEntitySet</span> <span class="o">==</span> <span
class="kc">null</span><span class="o">)</span> <span class="o">{</span>
+ <span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Entity not found."</span><span class="o">,</span>
+ <span class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">NOT_FOUND</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">(),</span> <span
class="n">Locale</span><span class="o">.</span><span
class="na">ROOT</span><span class="o">);</span>
+ <span class="o">}</span>
+
+ <span class="n">Integer</span> <span class="n">amount</span> <span
class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span
class="na">parseInt</span><span class="o">(</span><span
class="n">uriResourceFunction</span><span class="o">.</span><span
class="na">getParameters</span><span class="o">().</span><span
class="na">get</span><span class="o">(</span><span class="mi">0</span><span
class="o">).</span><span class="na">getText</span><span class="o">())</span>
+ <span class="c1">// then fetch the entity collection for the target
type</span>
+ <span class="n">responseEntity</span> <span class="o">=</span> <span
class="n">storage</span><span class="o">.</span><span
class="na">getBoundFunctionEntity</span><span class="o">(</span><span
class="n">function</span><span class="o">,</span> <span
class="n">amount</span><span class="o">);</span>
+ <span class="o">}</span>
+<span class="o">}</span>
+
+<span class="k">if</span> <span class="o">(</span><span
class="n">responseEntity</span> <span class="o">==</span> <span
class="kc">null</span><span class="o">)</span> <span class="o">{</span>
+ <span class="c1">// this is the case for e.g. DemoService.svc/Categories(4)
or DemoService.svc/Categories(3)/Products(999)</span>
+ <span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Nothing found."</span><span class="o">,</span> <span
class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">NOT_FOUND</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">(),</span> <span
class="n">Locale</span><span class="o">.</span><span
class="na">ROOT</span><span class="o">);</span>
+<span class="o">}</span>
+
+<span class="c1">// 3. serialize</span>
+<span class="n">ContextURL</span> <span class="n">contextUrl</span> <span
class="o">=</span> <span class="n">ContextURL</span><span
class="o">.</span><span class="na">with</span><span class="o">().</span><span
class="na">entitySet</span><span class="o">(</span><span
class="n">responseEdmEntitySet</span><span class="o">).</span><span
class="na">suffix</span><span class="o">(</span><span
class="n">Suffix</span><span class="o">.</span><span
class="na">ENTITY</span><span class="o">).</span><span
class="na">build</span><span class="o">();</span>
+<span class="n">EntitySerializerOptions</span> <span class="n">opts</span>
<span class="o">=</span> <span class="n">EntitySerializerOptions</span><span
class="o">.</span><span class="na">with</span><span class="o">().</span><span
class="na">contextURL</span><span class="o">(</span><span
class="n">contextUrl</span><span class="o">).</span><span
class="na">build</span><span class="o">();</span>
+
+<span class="n">ODataSerializer</span> <span class="n">serializer</span> <span
class="o">=</span> <span class="n">odata</span><span class="o">.</span><span
class="na">createSerializer</span><span class="o">(</span><span
class="n">responseFormat</span><span class="o">);</span>
+<span class="n">SerializerResult</span> <span
class="n">serializerResult</span> <span class="o">=</span> <span
class="n">serializer</span><span class="o">.</span><span
class="na">entity</span><span class="o">(</span><span
class="n">serviceMetadata</span><span class="o">,</span>
+ <span class="n">responseEdmEntityType</span><span class="o">,</span> <span
class="n">responseEntity</span><span class="o">,</span> <span
class="n">opts</span><span class="o">);</span>
+
+<span class="c1">// 4. configure the response object</span>
+<span class="n">response</span><span class="o">.</span><span
class="na">setContent</span><span class="o">(</span><span
class="n">serializerResult</span><span class="o">.</span><span
class="na">getContent</span><span class="o">());</span>
+<span class="n">response</span><span class="o">.</span><span
class="na">setStatusCode</span><span class="o">(</span><span
class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">OK</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">());</span>
+<span class="n">response</span><span class="o">.</span><span
class="na">setHeader</span><span class="o">(</span><span
class="n">HttpHeader</span><span class="o">.</span><span
class="na">CONTENT_TYPE</span><span class="o">,</span> <span
class="n">responseFormat</span><span class="o">.</span><span
class="na">toContentTypeString</span><span class="o">());</span>
+<span class="o">}</span>
+</pre></div>
+
+
<h3 id="implement-an-action-processor">Implement an action processor<a
class="headerlink" href="#implement-an-action-processor" title="Permanent
link">¶</a></h3>
<p>Create a new class <code>DemoActionProcessor</code> make them implement the
interface interface 'ActionEntityCollectionProcessor' and
'ActionEntityProcessor'.</p>
<div class="codehilite"><pre> <span class="kd">public</span> <span
class="kd">class</span> <span class="nc">DemoActionProcessor</span> <span
class="kd">implements</span> <span
class="n">ActionEntityCollectionProcessor</span><span class="o">,</span> <span
class="n">ActionEntityProcessor</span> <span class="o">{</span>
@@ -405,7 +715,7 @@ After finishing the implementation the d
<span class="n">UriResourceEntitySet</span> <span
class="n">boundEntitySet</span> <span class="o">=</span> <span
class="o">(</span><span class="n">UriResourceEntitySet</span><span
class="o">)</span> <span class="n">resourcePaths</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">0</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span
class="n">resourcePaths</span><span class="o">.</span><span
class="na">size</span><span class="o">()</span> <span class="o">></span>
<span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// Check if there is a navigation segment added after the
bound parameter</span>
- <span class="k">if</span> <span class="o">(</span><span
class="n">resourcePaths</span><span class="o">.</span><span
class="na">get</span><span class="o">(</span><span class="mi">1</span><span
class="o">)</span> <span class="k">instanceof</span> <span
class="n">UriResourceNavigation</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="n">resourcePaths</span><span class="o">.</span><span
class="na">get</span><span class="o">(</span><span class="mi">1</span><span
class="o">)</span> <span class="k">instanceof</span> <span
class="n">UriResourceAction</span><span class="o">)</span> <span
class="o">{</span>
<span class="n">action</span> <span class="o">=</span> <span
class="o">((</span><span class="n">UriResourceAction</span><span
class="o">)</span> <span class="n">resourcePaths</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">2</span><span class="o">))</span>
<span class="o">.</span><span class="na">getAction</span><span
class="o">();</span>
<span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Action "</span> <span class="o">+</span> <span
class="n">action</span><span class="o">.</span><span
class="na">getName</span><span class="o">()</span> <span class="o">+</span>
<span class="s">" is not yet implemented."</span><span
class="o">,</span>
@@ -487,8 +797,8 @@ After finishing the implementation the d
<span class="n">UriResourceEntitySet</span> <span
class="n">boundEntity</span> <span class="o">=</span> <span
class="o">(</span><span class="n">UriResourceEntitySet</span><span
class="o">)</span> <span class="n">resourcePaths</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">0</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span
class="n">resourcePaths</span><span class="o">.</span><span
class="na">size</span><span class="o">()</span> <span class="o">></span>
<span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// Checks if there is a navigation segment added after
the binding parameter</span>
- <span class="k">if</span> <span class="o">(</span><span
class="n">resourcePaths</span><span class="o">.</span><span
class="na">get</span><span class="o">(</span><span class="mi">1</span><span
class="o">)</span> <span class="k">instanceof</span> <span
class="n">UriResourceNavigation</span><span class="o">)</span> <span
class="o">{</span>
- <span class="n">action</span> <span class="o">=</span> <span
class="o">((</span><span class="n">UriResourceAction</span><span
class="o">)</span> <span class="n">resourcePaths</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">2</span><span class="o">))</span>
+ <span class="k">if</span> <span class="o">(</span><span
class="n">resourcePaths</span><span class="o">.</span><span
class="na">get</span><span class="o">(</span><span class="mi">1</span><span
class="o">)</span> <span class="k">instanceof</span> <span
class="n">UriResourceAction</span><span class="o">)</span> <span
class="o">{</span>
+ <span class="n">action</span> <span class="o">=</span> <span
class="o">((</span><span class="n">UriResourceAction</span><span
class="o">)</span> <span class="n">resourcePaths</span><span
class="o">.</span><span class="na">get</span><span class="o">(</span><span
class="mi">1</span><span class="o">))</span>
<span class="o">.</span><span class="na">getAction</span><span
class="o">();</span>
<span class="k">throw</span> <span class="k">new</span> <span
class="nf">ODataApplicationException</span><span class="o">(</span><span
class="s">"Action "</span> <span class="o">+</span> <span
class="n">action</span><span class="o">.</span><span
class="na">getName</span><span class="o">()</span> <span class="o">+</span>
<span class="s">" is not yet implemented."</span><span
class="o">,</span>
<span class="n">HttpStatusCode</span><span class="o">.</span><span
class="na">NOT_IMPLEMENTED</span><span class="o">.</span><span
class="na">getStatusCode</span><span class="o">(),</span> <span
class="n">Locale</span><span class="o">.</span><span
class="na">ENGLISH</span><span class="o">);</span>
@@ -551,6 +861,15 @@ After finishing the implementation the d
<h2 id="run-the-implemented-service">Run the implemented service<a
class="headerlink" href="#run-the-implemented-service" title="Permanent
link">¶</a></h2>
<p>After building and deploying your service to your server, you can try the
following requests:</p>
+<p><strong>Functions (Called via GET)</strong> </p>
+<ul>
+<li>
+<p><a
href="http://localhost:8080/DemoService-Action/DemoService.svc/Categories/OData.Demo.GetDiscountedProducts(Amount=50)">http://localhost:8080/DemoService-Action/DemoService.svc/Categories/OData.Demo.GetDiscountedProducts(Amount=50)</a></p>
+</li>
+<li>
+<p><a
href="http://localhost:8080/DemoService-Action/DemoService.svc/Categories(0)/OData.Demo.GetDiscountedProduct(Amount=50)">http://localhost:8080/DemoService-Action/DemoService.svc/Categories(0)/OData.Demo.GetDiscountedProduct(Amount=50)</a></p>
+</li>
+</ul>
<p><strong>Actions (Called via POST)</strong> <br />
<em>Note:</em> Set the Content-Type header to: <code>Content-Type:
application/json</code></p>
<ul>
@@ -565,10 +884,6 @@ After finishing the implementation the d
<p>{ "Amount": 50 }</p>
</li>
</ul>
-<p>To verify that the service has been reseted, you can request the collection
of products</p>
-<ul>
-<li><a
href="http://localhost:8080/DemoService-Action/DemoService.svc/Products">http://localhost:8080/DemoService-Action/DemoService.svc/Products</a></li>
-</ul>
<h1 id="links">Links<a class="headerlink" href="#links" title="Permanent
link">¶</a></h1>
<h3 id="tutorials">Tutorials<a class="headerlink" href="#tutorials"
title="Permanent link">¶</a></h3>
<p>Further topics to be covered by follow-up tutorials:</p>