-
Notifications
You must be signed in to change notification settings - Fork 21
Design Overall
Prev: Approach |
From a high-level point of view, ODataJClient consists of two distinct layers:
-
Engine
Low-level communication layer taking care of actual REST communication and OData entity (de)serialization, exposing methods to hook into the OData protocol for manipulating entities and invoking actions and functions. -
Proxy
This layer will convert any local change to POJOs and any local invocation of annotated interfaces' methods into actual calls to the Engine layer.
ODataJClient engine is there for Java developers that needs to access underlying details of the OData communication protocol. This allows great customization and the possibility to dig into the actual data exchange.
ODataJClient proxy mode is thought for experienced Java developers which are familiar with widespread Java Enterprise and / or Open Source technologies and prefer to interact with OData services at a very abstract level. This allows easy domain integration with complex architectures.
-
Generate POJOs (e.g. Java classes, enums and interfaces) using the
POJOGenerator
tool
This tool will grab the metadata document from a given URI and create annotated classes, enums and intefaces in the provided package. -
Include the generated Java files into an existing (or new) Java project: such files need no changes and are immediately available regardless of the IDE or the build system (e.g. Ant, Maven, Gradle, ...) used.
In order to show a concrete example of this use case, some sample files were generated from the OData demo; some excerpts will be shown below, but the whole set is anyway available for further analysis.
The central element is the EntityContainer
:
@EntityContainer(name = "DemoService", isDefaultEntityContainer = true)
public interface DemoService {
Products getProducts();
Categories getCategories();
Suppliers getSuppliers();
@FunctionImport(name = "GetProductsByRating", entitySet = Products.class,
returnType = "Collection(ODataDemo.Product)")
Collection<Product> getProductsByRating(
@Parameter(name = "rating", type = "Edm.Int32", mode = ParameterMode.In) Integer rating);
}
For an EntitySet
as Products
only some details (e.g. the OData name) are needed, since any available operation is defined in the super interface:
@EntitySetName("Products")
public interface Products extends EntitySet<Product, Integer> {
}
Generating an EntityType
, with all associated properties and navigation is straightforward:
@EntityType("Product")
public class Product implements Serializable {
private static final long serialVersionUID = -7176997693842768563L;
@Key
@Property(name = "ID", type = "Edm.Int32", nullable = false)
private Integer id;
@Property(name = "Name", type = "Edm.String", nullable = true,
fcTargetPath = "SyndicationTitle", fcContentKind = EdmContentKind.text, fcKeepInContent = false)
private String name;
@Property(name = "Description", type = "Edm.String", nullable = true,
fcTargetPath = "SyndicationSummary", fcContentKind = EdmContentKind.text, fcKeepInContent = false)
private String description;
@Property(name = "ReleaseDate", type = "Edm.DateTime", nullable = false)
private Date releaseDate;
@Property(name = "DiscontinuedDate", type = "Edm.DateTime", nullable = true)
private Date discontinuedDate;
@Property(name = "Rating", type = "Edm.Int32", nullable = false)
private Integer rating;
@Property(name = "Price", type = "Edm.Decimal", nullable = false)
private Float price;
@NavigationProperty(name = "Category", relationship = "ODataDemo.Product_Category_Category_Products",
fromRole = "Product_Category", toRole = "Category_Products")
private Category category;
@NavigationProperty(name = "Supplier", relationship = "ODataDemo.Product_Supplier_Supplier_Products",
fromRole = "Product_Supplier", toRole = "Supplier_Products")
private Supplier supplier;
...
}
Naturally, since any OData element is contained in a namespace, there will also be a package-info.java
file like as
@Namespace("ODataDemo")
package com.msopentech.odatajclient.proxy.odatademo;
so that any Java enum, class or interface in this package is considered part of the annotated OData namespace.
All that is reported above is also available in an asynchronous flavor: the central element is again a slightly different EntityContainer
:
@EntityContainer(name = "DemoService", isDefaultEntityContainer = true)
public interface AsyncDemoService {
AsyncProducts getProducts();
AsyncCategories getCategories();
AsyncSuppliers getSuppliers();
@FunctionImport(name = "GetProductsByRating", entitySet = Products.class,
returnType = "Collection(ODataDemo.Product)")
Future<Collection<Product>> getProductsByRating(
@Parameter(name = "rating", type = "Edm.Int32", mode = ParameterMode.In) Integer rating);
}
where asynchronous operations are accessible as follows:
@EntitySetName("Products")
public interface AsyncProducts extends AsyncEntitySet<Product, Integer> {
}
Some sample usage patterns of the elements defined above are available.
In order to provide transparent back and forth translation of high-level POJOs into low-level OData protocol entities, an Aspect Oriented Programming (AOP) approach is to be followed. AOP declarative processing, in fact, seems to fit particularly well with the usage scenario defined above.
ODataJClient will provide a whole set of aspects, join points and AOP proxy objects so that any invocation to generated POJOs will be intercepted and turned into actual calls to the underlying Engine layer.
Prev: Approach |