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.
Web Determinations session data can be saved to any data sources that a Java/.NET app can connect to. This ranges from simple file systems to databases, and also large applications that expose their data source via API's.
A new Web Determinations session can be created and pre-seeded with rule data from data sources.
A user's current Web Determinations session can be persisted to data sources. The user is able to specify the Save ID, or save to the current Save ID.
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.
How Web Determinations uses a Data Adaptor plugin by default
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:
A list of available case ID's to load is displayed to the user.
The user selects the caseID to load from a list.
Web Determinations server retrieves the Data Adaptor registered to the current Web Determinations interview.
Web Determinations Server calls the Data Adaptor load(), passing the caseID in together with the current Interview's rulebase.
Data Adaptor load() returns an object that contains session data back to Web Determinations server.
Web Determinations server starts a new session and loads the returned data into it.
When the user saves using the default method, the following occurs:
The user clicks on either Save or Save As.
If the user chooses to Save and there is no CaseID assigned to the current session, the user specifies the CaseID using the Save As screen. Otherwise the current session Case ID is used.
If the user chooses to Save As, the user specifies the CaseID to use for the save in the Save As screen.
Web Determinations server retrieves the Data Adaptor registered to the current Web Determinations interview.
The CaseID from Step 1a/b is used by Web Determinations server to call the Data Adaptor save(). The Web Determinations server also passes in the Web Determinations interview session data.
The Data Adaptor performs the save onto the datasource, and returns a Case ID.
The Web Determinations server uses the Case ID returned and assigns it onto the current Web Determinations interview session.
Loading by URL
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.
webdeterminations_webapp - the address of the Web Determinations web application for example, localhost/webdeterminations/.
rulebase - the name of the target rulebase installed in the current Web Determinations (see Web Determinations Server setup to locate the rulebase folder).
locale - the locale for the Intervew session, for example, en-US, es-CR for Spanish - Costa Rica.
goalID - optional, If it is provided, an investigation on the provided goalID is started and thus skips the Summary screen.
Goal IDs are structured like, for example, for the Attribute 'eligible' in global, it is 'Attribute~eligible~global~global'.
caseID - the Case ID to be provided by the client system. The client system may already have the data needed to retrieve/cosntruct the Case ID, or the client system may authenticate the user retrieve or generate the Case ID.
Data Adaptor plugin and other Web Determinations plugins
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:
Web Determinations extensions should start a new session before loading the data into the Web Determinations session (this is the same as how the default load process works). Loading session data into an existing Web Determinations session that already has data will result in abnormal behavior.
The Data Adaptor load() and save() only have rulebase model data (for load) and rulebase model + instance data (for save) passed in. This means that Web Determinations extensions that call the Data Adaptor load() and save() cannot pass in additional data, it can only trigger the Data Adaptor to load or save. Therefore the Data Adaptor needs to be designed in such a way that it can correctly load data based on the rulebase model data available, or correctly save data based on rulebase model plus instance data.
The Web Determinations extension calling the Data Adaptor must provide the Case ID for load(), and also for save() if the Data Adaptor is not configured to provide Case ID's. Keep in mind that there times where the save() may be called by a Web Determinations Extension and the Case ID has not been provided yet (for example, the Web Determinations interview was not started with a Data Adaptor load())
Error handling
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:
A specific 'Failed to Load' screen is displayed by Web Determinations, together with the attempted Case ID if the Data Adaptor load() throws an exception.
When the Data Adaptor save method is called:
A specific 'Failed to Save' screen is displayed by Web Determinations if the Case ID returned by save() is null or empty.
Generic 'error' page if the save() throws an exception.
Data Adaptor class methods
A Data Adaptor plugin implements the DataAdaptorPlugin interface. The DataAdaptorPlugin interface in turn extends the DataAdaptor interface and also the InterviewSessionPlugin interface.
TheDataAdaptor interface requires the following when implemented:
The SecurityToken can be used by the plugin developer for authorization.
The caseID is the ID used by the developer to retrieve the Web Determinations session data to load.
The InterviewRulebase allows the plugin developer to access the session's rulebase, and its contents; that is, entities, attributes, relationships, screens, flows, and so on.
an object of InterviewUserData is returned, which in turn is loaded by the calling process.
The SecurityToken can be used by the plugin developer for authorization.
The caseID is used as the ID to save the Web Determinations Session data. This is optional if the dataAdaptor is generating it's own ID during the save.
The InterviewSession contains the Web Determinations session, which has the data to save and also data about the current session; for example, the rulebase being used.
The caseID used by the save() is returned.
boolean dataAdaptorProvidesCaseID
This is set by the plugin developer to true if the dataAdaptor will provide the caseID (usually for first-time save). When this is true, the Save As button is not displayed, therefore the user can only initiate Save action.
This is used for setups where the datasource of the Data Adaptor will generate the CaseID; for example, SQL insert into a table with autoincrement primary key.
For default setups where the Web Determinations server calls the Data Adaptor save() (and not another Web Determinations extension), the CaseID passed will be null/empty since the user was not able to provide the Case ID.
The SecurityToken can be used by the plugin developer for authorization.
The InterviewRulebase allows the plugin developer to access the session's rulebase, and its contents; that is, entities, attributes, relationships, screens, flows, and so on.
The String array returned is a list of Case ID's to display to the user as options of cases available to be loaded.
Factors to consider when designing a Data Adaptor plugin
When designing a Data Adaptor, the following factors needs to be analyzed
datasource type and schema/setup.
rulebase complexity and rule data required to be loaded/saved.
the need to support flexibility/maintainability for both a changing rulebase and datasource schema.
Essentially, the Data Adaptor needs to be able to:
for loading - retrieve data from the datasource so it can be transformed into Web Determinations session instance data, and loaded onto the session.
for saving - manipulate the Web Determinations session instance data so it can be persisted into the datasource.
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.
About the data passed into the Data Adaptor methods
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 rulebase model data- The 'metadata'. This is the entities, attributes, and relationships created when a rule author creates a rulebase in Oracle Policy Modeling. It provides the data structure that allows the Determinations Engine to inference, get next question, and so on.
The rulebase instance data - this is the actual data provided by the user during a Web Determinations session, and maps to items in the rulebase data model.
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:
InterviewRulebase - this object provides access to the rulebase model data.
(Rule)Session - this object provides access to the rulebase instance data.
How to use the data passed into the Data Adaptor methods
The Data Adaptor load() receives the InterviewRulebase, therefore it only has access to the rulebase model data.
the model data is used together with the data retrieved from the data source to construct the InterviewUserData object to be loaded into the Web Determinations session.
the model data can be used to drive the retrieval process from the datasource via mapping/configuration data for data integrity and better maintainability and flexibility of the Data Adaptor.
Sample code to demonstrate access of the InterviewRulebase model data
public InterviewUserData load(SecurityToken token, String caseID, InterviewRulebase rulebase) { ... //Go through the rulebase entities, and get all instances of each entity in the database together with //attributes Rulebase rulebaseDE = rulebase.getRulebase(); //note you need to get the Determination Engine rulebase first for(Object entObj : rulebaseDE.getEntities()) { //The code below accesses an Entity in the List of Entities, its Attributes, and Relationships Entity entityModel = (Entity)entObj; List<Attribute> entityAttributes = entityModel.getAttributes(); List<Relationship> entityRelationships = entityModel.getRelationships(); } ... }
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().
the model data must be used to extract the instance data from the (Rule)Session.
the model data is used to persist the extracted instance data to the data source.
the model data can also be used with mapping/configuration data for better Data Adaptor maintainability and flexibility.
Sample code to demonstrate usage of the InterviewSession model data
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); } } } ... }
How to build the InterviewUserData object in Data Adaptor load()
The InterviewUserData is much simpler than the InterviewSession or InterviewRulebase. The InterviewUserData has a List of Entities and Relationship instances.
The InterviewUserData is made up of InterviewEntityInstance, which in turn is made up of relationships and attributes.
To add an Entity instance to the InterviewUserData, call the method addInstance(InterviewEntityInstance entInstance) of the InterviewUserData
To add a relationship to an Entity instance, call the addRelationship(Relationship rel, InterviewEntityInstance target, byte status) on the InterviewEntityInstance object.
An Entity instance stores its attributes in a map as key/value pairs, where the key is the Attribute's public name. Adding an attribute to the Entity instance is as simple as calling its setValue(String key, Object value) method.
Note that for the Entities and Relationships to be added into the session properly, they must be set to the correct status.
For relationships, it is done when calling the addRelationship() by passing the 'Add' status; for example, entInstance.addRelationship(relationship, targetEntInstance, InterviewInstanceStatus.ADD);
For entities, the entity instance's status is set to 'Add'; for example, entInstance.setStatus(InterviewInstanceStatus.ADD);
Note that when adding a relationship to an Entity Instance; for example, entInstance.addRelationship(relationship, targetEntInstance, InterviewInstanceStatus.ADD);
the reverse relationship is also created for the target instance - targetEntInstance.
Sample code to demonstrate usage of the InterviewUserData
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'
General Data Adaptor functionality and design concepts
Authentication
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.
Identifying Entity instances from each other
There are a couple of uses for instance Identifiers in the Data Adaptor:
managing the instance ID of the Entity instances being created (for load) or read (for save) to be able to create or persist relationships properly.
in cases where the instance ID is required to check if it exists in the datasource for a Data Adaptor save().
use the instance ID for complex Data Adaptor designs/architectures such as mapping the rulebase model data to the database schema.
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.
Ways to make the Data Adaptor flexible to rulebase and datasource changes
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 mapping can contain rulebase entity/attribute/relationships that should be retrieved, and how to retrieve it in the datasource
having a mapping as a configuration means it can also be easily updated, and integrated with rule authoring workflow for automatic update
Other Data Adaptor load() specific concepts
The following is a list of things to note when developing the Data Adaptor load():
check that the caseID exists in the datasource.
the Global instance is automatically created when an instance of InterviewUserData is created.
retrieved via InterviewUserData.getGlobalInstance();.
also the Global instance doesn't need its InterviewInstanceStatus set to ADD.
it's a good idea to drive the data retrieval process from the data source via the rulebase model data; that is, loop through the model data (entities, its attributes, relationships), through the loop if an expected model comes up then perform logic.
note that for quick setups (for example, prototypes and proof of concepts), it's possible to just hard code rulebase and datasource references so that it simply retrieves data, builds the user data, and returns it.
Relationships
Entity Relationships is the more complex part of loading data into a Web Determinations session.
adding a relationship to an entity instance is done via the call: sourceEntityInstance.addRelationship(Relationship rel, InterviewEntityInstance target, InterviewEntityStatus byte).
adding the relationship is usually done to the source entity instance, and doing so automatically creates the reverse relationship for the target.
relationships between the Global entity and other entities must always be created. Otherwise entities not attached to the Global will not appear in the Web Determinations session.
relationships between entities can be left out to give the user a pre-set choice when linking them (with the help of screens and screenflow); for example, say a child is connected to a school entity via an m-1 a child's school.
child and school entities can be loaded into the session, but not the relationships between them
during the interview, the user will be able to select which school each child goes to if the specified school was loaded in session, rather than having to create instances of the school first.
both source and target entity instances and the Relationship object (model data of the relationship) are needed to add a relationship.
as mentioned before, the status needs to be InterviewInstanceStatus.ADD so it is added to the session
when creating instance relationships, it might be handy to create a list that contains five datamembers for each item - source entityID, source instanceName, Relationship object or name, target entityID and target instanceName.
this list can be populated while retrieving entity instances and their attributes.
the list can be processed after all instance objects have been created, to create the relationships and link the source/target entities.
Other Data Adaptor save() specific concepts
The following is a list of handy notes for reference when developing the Data Adaptor save()
The instance identifier is important here.
This is important for implementations where the Data Adaptor save() needs the identifier to know where to save the data (for example, saving the data to a specific table row).
Therefore it is imperative that the Data Adaptor load() correctly assigns instanceNames; for example, Columns 1-5 from table 'User', userID '1' is loaded, and inferenced data is saved on columns 6-10 of the same userID '1' in the table 'User'.
Its more convenient to process the relationships first, then process entities and attributes.
Go through all relationship instances and store it in a List object, then while going through entities and persisting them any relationship data that needs to be persisted can be done together with the instance.
Opposite to Data Adaptor load(), where the relationship is processed last.
Similar to the Data Adaptor load(), a list with 5 datamembers (source ID/name, target ID/name, and Relationship object) or similar list would do.
After the list is populated, while saving the entities and attributes the relationship data can also be saved, thus it's only one insert/update SQL instead of two in the case of databases.
A tool or intermediate architecture/API between the Data Adaptor and the datasource would be handy here, to make data persistence easier to manage - especially with changing rulebase and datasource; for example, for SQL databases, an ORM would reduce the amount of maintenance work on SQL query building.
Miscellaneous Notes
If multiple Data Adaptor plugins have been registered for a Web Determinations session, there is no way to specify which one will be used by the Interview Engine. It is up to the developer to ensure that only one Data Adaptor is loaded for a Web Determinations session.
A developer can deploy multiple Data Adaptor's into a Web Determinations server, and can use the Data Adaptor's plugin method implementation getInstance() to ensure that only one Data Adaptor is registered for a Web Determinations session.
Entity instances of the target for an m-m relationship without creating the relationship between the source/target allows the source to select a predefined list of the target -> the effect is a poor man's List Provider.
Link template to load a Case ID - http://<webserver address>/web-determinations/startsession/<rulebase name>/<locale ID>?caseID=<caseID#>&user=guest.
Web Determinations Extensions can only call Data Adaptor load and save, it cannot pass in additional data.