The Data Adaptor is an Engine Session plugin to the Web Determinations Interview Engine which allows the current Web Determinations session to use an arbitrary data source for loading and saving session data.
The Web Determinations server only loads one Data Adaptor plugin when a Web Determinations session is started. The Web Determinations server ships with a Data Adaptor plugin, called XDSDataAdaptor. By default this plugin is loaded unless another Data Adaptor plugin has been registered in the current Web Determinations session, in which case the new (custom) Data Adaptor plugin will be loaded.
The Data Adaptor is a plugin, therefore it follows plugin implementations and its benefits/restrictions.For more information about plugins, see Introduction to plugins. For more information about Interview Engine, see Understand the Interview Engine.
The XDS Data Adaptor loads and saves data with the .xds format. This is useful for testing purposes as a Web Determinations session can load data that was saved from, for example, a Debugger session, and likewise a Debugger session can load data from a Web Determinations session.
The examples are for Data Adaptor plugins based on Java programming, but the concepts apply to both Java and .NET.
For more information about implementing the Data Adaptor to common integration scenarios, please see Data Adaptor - common scenarios.
Data Adaptor and the Web Determination architecture
Developing a Data Adaptor plugin for your specific project/datasource
General Data Adaptor functionality and design concepts
Plugin loading, invocation and discovery
Plugins - general technical information
Data Adaptor - common scenarios
Data Adaptor - sample code (DerbyDataAdaptor)
Data Adaptor - sample code (Autosave with Derby)
This section details how the Data Adaptor fits into the Web Determinations architecture, and how to use it in the Web Determinations environment.
How Web Determinations uses a Data Adaptor plugin by default
Data Adaptor plugin and other Web Determinations plugins
A Data Adaptor plugin is used whenever the user attempts to save or load Web Determinations session data. By default, the user saves/loads by clicking on the default 'Save/Load/Save As' buttons during a Web Determinations session.
When the user loads data using the default method, the following occurs:
When the user saves using the default method, the following occurs:
Web Determinations also allows a case to be loaded automatically via a URL template. Using the URL skips the step where the user has to choose which Case ID to load. Instead the user starts the Web Determinations interview with the data loaded already. This is useful for integrating Web Determinations with other web applications.
http://<webdeterminations_webapp>/startsession/<rulebase>/<locale>/<goalID>?caseID=<caseID#>&user=guest
Other ways in which the Data Adaptor is used in Web Determinations is through other Web Determinations plugins. These plugins can call the Data Adaptor's save and load methods, thus allowing loading data into the Web Determinations interview/saving data in the current Web Determinations interview to be controlled by plugins such as Event Handlers. Saving of session data can happen automatically without the user needing to explicitly issue a save command (which would be the most common case when Web Determinations is integrated within a web application/workflow).
Web Determinations plugins can access the Data Adaptor registered to the current Web Determinations session if the plugin has access to the SessionContext object or its member the InterviewSession object. The Web Determinations plugin can call the Data Adaptor load() via the InterviewSession object to retrieve InterviewUserData based on the provided caseID, or call the Data Adaptor save() to persist the current InterviewSession; for example:
Calling the Data Adaptor load
InterviewUserData InterviewUserData = sessionContext.getInterviewSession().getDataAdaptor().load(token, caseIDToLoad, sessionContext.getInterviewSession().getRulebase());
Calling the Data Adaptor save
interviewSession.getDataAdaptor().save(token, caseIDToSave, interviewSession);
Notes
Data Adaptor plugins can handle errors that occur within it (for example, SQLExceptions), and then throw an exception for the calling method to handle (or in Java, an unchecked exception).
When the Data Adaptor load method is called and an error occurs:
When the Data Adaptor save method is called:
A Data Adaptor plugin implements the DataAdaptorPlugin interface. The DataAdaptorPlugin interface in turn extends the DataAdaptor interface and also the InterviewSessionPlugin interface.
The DataAdaptor interface requires the following when implemented:
Method Signature |
Description |
---|---|
InterviewUserData load(SecurityToken token, String caseID, InterviewRulebase rulebase) |
|
String save(SecurityToken token, String caseID, InterviewSession session) |
|
boolean dataAdaptorProvidesCaseID |
|
String[] listCases(SecurityToken token, InterviewRulebase rulebase) |
|
For requirements from the plugin interface, see Plugin loading, invocation and discovery See the sample code section for practical examples
This section explains the various approaches on designing and developing a Data Adaptor plugin for a specific project/rulebase/datasource.
Factors to consider when designing a Data Adaptor plugin
About the data passed into the Data Adaptor methods
How to use the data passed into the Data Adaptor methods
How to build the InterviewUserData object in Data Adaptor load()
When designing a Data Adaptor, the following factors needs to be analyzed
Essentially, the Data Adaptor needs to be able to:
The design of a Data Adaptor plugin varies because how it will map the datasource data to its equivalent Web Determinations session data depends on the datasource and rulebase setup/complexity. Also the maintainability aspect of the Data Adaptor is important - because for both rulebase and datasource change it is important to design the Data Adaptor to match the anticipated changes both will have in the future.
For example - the Data Adaptor might have to deal with a very complex rulebase and database system that is anticipated to change. It will probably be designed with an architecture, using other tools/libraries, etc to ensure that the it can meet various requirements such as authentication, datasource transaction, errors, as well as non-functional such as performance. On the other hand the rulebase and datasource might be small and simple in which case the Data Adaptor simply needs to be a small implementation, with a lot of hard coded data to access the datasource.
It is important to understand the available data that is passed to the Data Adaptor load() and save().The input arguments objects that are of interest for both are the InterviewSession and InterviewRulebase objects. The SecurityToken and caseID are both sufficiently explained in the previous section Programming Constructs.
But first, it is important to understand the two main types of data that exists during Determination Engine runtime:
The following table shows the difference between the Model and Instance:
Rulebase Model data |
Rulebase Instance data |
---|---|
the child (entity) |
child1 (identifier value = Wayne) |
the school (entity) |
school2 (identifier value = Summer High) |
the child's name (attribute) |
Wayne |
the child's school (relationship) |
<child1 connects to school2> |
The InterviewSession object contains many members, but with relation to the Data Adaptor it's useful members are:
The Data Adaptor load() receives the InterviewRulebase, therefore it only has access to the rulebase model data.
The Data Adaptor save() receives the InterviewSession object, so it has access to both the rulebase model and instance data. Note that it does not receive the Web Determinations session user data in a InterviewUserData object - the return object of the Data Adaptor load().
public String save(SecurityToken token, String caseID,
InterviewSession iSession)
{
...
Session sessionDE = session.getRuleSession(); //note you need to get the Determination Engine session
Rulebase rulebaseDE = sessionDE.getRulebase(); //this gets the Determination Engine rulebase
...
for(Object entObj : rulebaseDE.getEntities())
{
//Get all instances of the current entity from the session and loop trhough it
Entity currentEntity = (Entity)entObj;
Collection<EntityInstance> currentEntityInstances = currentEntity.getEntityInstances(sessionDE);
//note retrieval of instance using the Entity model object
Iterator iterateCurrentEntityInstances = currentEntityInstances.iterator();
while(iterateCurrentEntityInstances.hasNext())
{
EntityInstance currEntInstance = (EntityInstance)iterateCurrentEntityInstances.next();
//Access attribute instance values of the current Entity Instance
for(Object attrObj : currentEntity.getAttributes())
{
Attribute currAttr = (Attribute)attrObj;
//Attributes in an entity instance is accessed through the Attribute object and passing the Entity
//instance
Object attributeValue = currAttr.getValue(currEntInstance); //note retrieval of instance using the
Attribute model object
}
//Get source or target instance for each relationship of the current Entity model by passing the
//instance to the Relationship model data
for(Object relObj : currentEntity.getRelationships())
{
Relationship currRel = (Relationship)relObj;
//note retrieval of relationship source/targets using the Relationship model object
if(currRel.isSingleTarget) EntityInstance targetInstance = currRel.getTarget(currEntInstance);
if(currRel.isSingleSource) EntityInstance sourceInstance = currRel.getSource(currEntInstance);
}
}
}
...
}
The InterviewUserData is much simpler than the InterviewSession or InterviewRulebase. The InterviewUserData has a List of Entities and Relationship instances.
public InterviewuserData load(SecurityToken token, String caseID,
InterviewRulebase rulebase)
{
...
InterviewUserData returnUserData = new InterviewUserData();
//Go through the Entity model, and create an InterviewEntityInstance of each, add some
//attributes and relationship, and add to user data
Rulebase rulebaseDE = rulebase.getRulebase();
//note you need to get the Determination Engine rulebase first
int count = 1;
for(Object entObj : rulebaseDE.getEntities())
{
Entity entityModel = (Entity)entObj;
//create an Entity instance and add it to the user data
InterviewEntityInstance entInt = new InterviewEntityInstance(entityModel.getName(),
count);
count++;
entInt.setStatus(InterviewInstanceStatus.ADD);
returnUserData.addInstance(entInt);
//add an attribute to the entity instance
entInt.setValue("attributePublicID", attributeValue);
}
//add a relationship - lets assume there's an entity called 'child' and 'school', with a 1-m relationship
InterviewEntityInstance child = new InterviewEntityInstance("child", 100);
InterviewEntityInstance school = new InterviewEntityInstance("school", 2);
Relationship childSchoolRel = rulebaseDE.getRelationship("the child\'s school", "child");
child.addRelationship(childSchoolRel, school, InterviewInstanceStatus.ADD);
//Note that the reverse relationship e.g. the school's student is created/added to 'school'
...
}
More sample code at Data Adaptor - Pseudo Code, Data Adapter - Sample Code (Autosave with Derby) and Data Adaptor - Sample Code (DerbyDataAdaptor)
A SecurityToken is provided to the save(), load(), and listCases() for the Data Adaptor to use if it needs to authenticate the user; for example, the Data Adaptor may need to use the token to authenticate and connect to the datasource as well.
It is optional to use the SecurityToken.
There are a couple of uses for instance Identifiers in the Data Adaptor:
The instanceName of an Entity instance object (which is InterviewEntityInstance or EntityInstance) is it's unique identifier amongst all other Entity instances in aWeb Determinations session; for example, a School entity instance can have an instanceName of 's1'. Therefore no other instance, regardless of whether it is a School entity or not, may have an instanceName of 's1'. The instanceName can be anything from a simple number to composite text generated from attributes of the instance; for example, instanceName = firstName + lastName + dob.toString().
The instanceName of Entity instances loaded into the Web Determinations session is maintained - so any instanceNames from the Data Adaptor load() will still be intact for those same instances that are processed in Data Adaptor save()
For complicated setups, the Data Adaptor developer can implement their own method of ID'ing instances. The Data Adaptor can implement a complex/composite instanceName property generated from various data values of the instance or the Data Adaptor developer can create an Identifier object that can assist in ID management of instances.
Making the Data Adaptor flexible to rulebase and datasource changes varies wildly from each implementation, but there are approaches and tools that should work for most setups. One thing to note is that datasource, especially legacy database systems does no change often due to the number of other systems that depend on it. Rulebase changes can vary from project to project.
Using a tool that simplifies the Data Adaptor's data collection from the datasource will be useful for both managing datasource changes and also rulebase-datasource mapping; for example, Object-Relational Mapping for relational databases.
Ideally the Data Adaptor load and save can be driven via a rulebase-datasource mapping as opposed to hard coding rulebase and datasource mapping within the Data Adaptor logic. The mapping can be separate for Data Adaptor load and save.
The following is a list of things to note when developing the Data Adaptor load():
Entity Relationships is the more complex part of loading data into a Web Determinations session.
The following is a list of handy notes for reference when developing the Data Adaptor save()