Jump to: navigation, search

Difference between revisions of "Maynstall Database WebServices"

(Step 4: Using the Maynstall model context in your code)
 
(5 intermediate revisions by the same user not shown)
Line 95: Line 95:
 
<code>
 
<code>
 
     IModelContext context = ModelContext.createModelContext();
 
     IModelContext context = ModelContext.createModelContext();
     User user = context.get(User.class, "fred"); // get a user from the context
+
   
     Address address = user.getAddress(); // can trigger lazy loading of address object
+
    // get a user from the context
 +
     User user = context.get(User.class, "fred");  
 +
   
 +
    // if not loaded, can trigger lazy loading of address object
 +
     Address address = user.getAddress();
 +
   
 +
    // set an attribute locally on an object
 +
    address.setCity("AnotherCity"); 
 +
   
 +
    // saves the pending changes to the remote model context
 +
    context.persist();
 
</code>
 
</code>
 +
 +
=== Step 5: Optimizing access to objects ===
  
 
Note that if there are performance issues with a distant remote client (high latency network), it is possible to use a trigger pattern to allow data to be loaded on the same request of loading the user.
 
Note that if there are performance issues with a distant remote client (high latency network), it is possible to use a trigger pattern to allow data to be loaded on the same request of loading the user.
  
 
<code>
 
<code>
@Entity
+
    @Entity
@DynamicExposure(procedure = "loadUser")
+
    @DynamicExposure(procedure = "loadUser")
public class User {  
+
    public class User {  
    ...snip...
+
        ...snip...
}
+
    }
 
</code>
 
</code>
  
Line 114: Line 126:
  
 
<code>
 
<code>
public class LoadUserProcedure implements IModelContextProcedureHandler {
+
    public class LoadUserProcedure implements IModelContextProcedureHandler {
    public <T> ExecuteResult execute(IModelContext context, Class<T> clazz, String procedureName, String... args) throws Exception {
+
        public <T> ExecuteResult execute(
        ...snip...
+
            IModelContext context, Class<T> clazz, String procedureName,  
 +
                String... args) throws Exception {
 +
            ...snip...
 +
        }
 
     }
 
     }
}
 
 
</code>
 
</code>
  
Line 133: Line 147:
 
   </extension>
 
   </extension>
 
</code>
 
</code>
 +
 +
=== Step 6: Other concepts to review ===
 +
 +
The following are some key classes / concepts that are used in Maynstall.
 +
 +
; Default Depth
 +
: The depth used when loading objects or lazily loading content when no other explicit depth is specified.  On both the search and get calls, a depth can optionally be specified to control how much data is loaded.
 +
; On Demand Loading
 +
: An option of the model context that allows Maynstall to dynamically load objects and attributes on demand when a reference is walked or an attribute accessed that has not yet been accessed.  On demand loading leverages the default depth as specified in the model context instance.
 +
; Custom Depths
 +
: Allows creation of custom depths to control which set of attributes and/or relationships are returned from the server.  This allows a subset of data to be returned to the client, with the remaining data loaded on demand (if enabled).
 +
; IModelContextMeta
 +
: Provides information on the model including information such as available attributes, pending changes, etc.  Can also be used to modify the model generically instead of using reflection.
 +
; SearchCriteria
 +
: Allows for the specification of a search using constructs specified via method constructs instead of a string format.
 +
; IModelRequestInterceptor
 +
: Allows for security services to be integrated into the server restricting access and updating of objects in a model-aware (code-level) mechanism.

Latest revision as of 12:45, 3 November 2008

Introduction

Within Maynstall, there is a layer of database services which allow a JPA-style data model to be exposed via REST-style web services. These services provide a consistent client-side representation of the POJOs including the ability to modify objects on the client and then persist a transaction of changes to the server. The services also include the following:

  • Background loading of related objects upon reference to those objects
  • Blocking and non-blocking with events mode of loading data not yet available on the client
  • Meta information to inspect pending changes on the client side
  • Usage of JPA annotations to support understanding inter-object relationships
  • Default JPA provider on the server providing integration with JPA (aka EclipseLink) to automatically expose any object
  • Security interceptors to restrict reading, updating, deleting and creating objects based on user and/or attributes being changed
  • Registration of model providers allowing mixing of objects from remote WS-based services as well as local JPA DB connections
  • Triggers for loading a large portion of the model in a single request for optimization
  • Procedures that can be registered via extension point to allow complex Java implementations of queries on the server
  • Out-of-band loading of large binary attributes as well as out-of-band uploading of binary data avoiding expensive serialization in the XML payloads
  • Lazy loading of large binary attributes with out-of-band download
  • Custom depth specification to control amount of data retrieved upon access to an object

Getting Started

To get started with the Maynstall database layer for remote objects, use the attached project set on this Wiki page. It provides you with the right set of projects that you need to work with the services. Note you will also need to EclipseLink or another JPA provider in your workspace / Eclipse install to allow the Maynstall code to resolve the various dependencies.

Download Team Project Set - Media:MaynstallDBWS-projectSet.psf

Overview of Plug-ins

org.eclipse.maynstall.client.core
Provides base services for using REST-style web services, and in particular, services that are backed by a Maynstall-based server side (provides simple services for accessing and sending requests)
org.eclipse.maynstall.client.model.provider
Registers a provider of Maynstall model contexts that is backed by a remote set of web services including providing transactional commits over the loaded data model.
org.eclipse.maynstall.common.tracker
Required to support a dependency in org.eclipse.maynstall.common.ws, not technically used by the Maynstall database web services.
org.eclipse.maynstall.common.type
Provides a set of common types used on the client and server side by the data model and data services. Also includes a few references to enums used by the Maynstall provisioning technologies (and is not directly required by the Maynstall database web services).
org.eclipse.maynstall.common.util
Provides common utilities leveraged by Maynstall services including services for calculating checksums, dealing with DOM, and accessing files used to configure Maynstall.
org.eclipse.maynstall.common.ws
Provides a set of common web service abstractions for building REST-style web services; mainly used as an underlying layer for the data services supported by Maynstall, but can be used as a building block for other REST web services as well.
org.eclipse.maynstall.model.core
Provides the main abstraction of the IModelContext and supporting classes which can be used on the client and server side for accessing content from different providers, including remote REST-based provider, and the JPA provider.
org.eclipse.maynstall.model.provider.common
Provides the common abstractions used by the different providers to implement the different mechanisms (including custom) model objects.
org.eclipse.maynstall.server.model.provider
Provides a JPA implementation of the model provider for leveraging JPA model objects via both the Maynstall model context paradigm as well as typically tied in to the model provider web services for access from a client.
org.eclipse.maynstall.server.model.provider.ws
Provides a set of web services to support usage of any model providers remotely via REST-style web services including typical CRUD style object access simply using the JPA annotated objects.
org.eclipse.maynstall.server.persistence
Provides base services for using JPA models within the Maynstall web service abstractions.

Coding with the Maynstall DB Access

Step 1: Creating your model

To get started using the remote Maynstall DB access, you must first create your JPA-annotated POJOs that will be made available remotely via Maynstall. When doing this, note that Maynstall respects relationships like OneToMany, ManyToMany, etc. and these must be specified to allow Maynstall to correctly understand the relationships when sending the information to the server side (and when serializing the content back to the client).

Also, Maynstall intercepts the access to the various methods on the model objects. If you are going to implement utility methods on your POJOs, it is currently necessary to use the @ComputedAccessor annotation to flag that certain methods should be left as is, and not attempt to use the lazily loaded model that Maynstall is handling behind the scenes.

This is an area of potential improvement in Maynstall since the code could better understand the field-level definitions to determine what calls / accessors should be intercepted and how.

Step 2: Setting up your model mappings

In Maynstall in each process you have, you can independently map your model to providers. For instance, on the client-side of your application, your model can be mapped to the remote REST-style service whereas on the server, you can map your model to JPA. On either side, if you then use IModelContext, you can access the model objects independently of concern for whether objects are on the client or server. There are extra annotations that can be specified on the object, such as model loading triggers that may only be used on one side of the client.

  <extension
        point="org.eclipse.maynstall.model.core.modelContext">
     <modelContext
           Id="MyClientModelContext"
           provider="org.eclipse.maynstall.client.model.provider.DefaultClientModelContext">
     </modelContext>
  </extension>
  <extension
        point="org.eclipse.maynstall.model.core.modelObjectSet">
     <modelObjects
           modelContext="MyClientModelContext">
        <modelObject
              class="com.foo.bar.User">
        </modelObject>
        <modelObject
              class="com.foo.bar.Address">
        </modelObject>
     </modelObjects>
  </extension>

The code above maps the com.foo.bar.User object back to the default client-side model context which uses REST-style web services to retrieve objects from the server.

Step 3: Setting up Maynstall models for the server

For the server side, you need to follow the normal configuration of JPA within OSGi to make sure the JPA model objects are initialized correctly. Similar to the client-side mapping, on the server side objects must be mapped back to the JPAModelContext provider which tells Maynstall that when exposing the objects via the web services to use JPA provider for actually getting the data that is serialized over the wire.

Step 4: Using the Maynstall model context in your code

The following is an example usage of the Maynstall model context using an object that is specified via JPA.

   IModelContext context = ModelContext.createModelContext();
   
   // get a user from the context
   User user = context.get(User.class, "fred"); 
   
   // if not loaded, can trigger lazy loading of address object
   Address address = user.getAddress();  
   
   // set an attribute locally on an object
   address.setCity("AnotherCity");   
   
   // saves the pending changes to the remote model context
   context.persist(); 

Step 5: Optimizing access to objects

Note that if there are performance issues with a distant remote client (high latency network), it is possible to use a trigger pattern to allow data to be loaded on the same request of loading the user.

   @Entity
   @DynamicExposure(procedure = "loadUser")
   public class User { 
       ...snip...
   }

In this instance, the @DynamicExposure call is triggered when the client attempts to load the user object for the first time, instead of just loading the User object using the current default depth, the trigger is executed to load the user and associated objects that are typically used without requiring another round trip to the server. Note that procedures can be used outside of triggers for cases where the same set of the model objects may not be loaded the same use-case dependent.

The procedure needs to be defined on the server-side for the set of data to be exposed. For instance:

   public class LoadUserProcedure implements IModelContextProcedureHandler {
       public <T> ExecuteResult execute(
           IModelContext context, Class<T> clazz, String procedureName, 
               String... args) throws Exception {
           ...snip...
       }
   }

Finally, the procedure needs to be registered via extension point on the server.

  <extension
        point="org.eclipse.maynstall.model.core.modelProcedureHandler">
     <modelContextProcedure
           handler="com.foo.bar.server.procedure.LoadUserProcedure"
           name="loadUser"
           target="com.foo.bar.User">
     </modelContextProcedure>
  </extension>

Step 6: Other concepts to review

The following are some key classes / concepts that are used in Maynstall.

Default Depth
The depth used when loading objects or lazily loading content when no other explicit depth is specified. On both the search and get calls, a depth can optionally be specified to control how much data is loaded.
On Demand Loading
An option of the model context that allows Maynstall to dynamically load objects and attributes on demand when a reference is walked or an attribute accessed that has not yet been accessed. On demand loading leverages the default depth as specified in the model context instance.
Custom Depths
Allows creation of custom depths to control which set of attributes and/or relationships are returned from the server. This allows a subset of data to be returned to the client, with the remaining data loaded on demand (if enabled).
IModelContextMeta
Provides information on the model including information such as available attributes, pending changes, etc. Can also be used to modify the model generically instead of using reflection.
SearchCriteria
Allows for the specification of a search using constructs specified via method constructs instead of a string format.
IModelRequestInterceptor
Allows for security services to be integrated into the server restricting access and updating of objects in a model-aware (code-level) mechanism.