Using data services
In this topic ...
How do I...
Architect a Service Oriented Application
Create pages that test a service provider?
Work disconnected from back-end data?
Create a service operation based on an integration Builder?
Use an Action List, Method, or LJO method in an operation?
Transform data returned by a service?
Add custom input or result processing to an operation?
Use a Service Mapping Registry file?
Related Topics...
The Factory provides the Services category of Builders we can use to create, test, use, and document data services. These Builders rely on various data integration Builders (SQL Call, SAP Function Call, Domino View, etc.) to access data from a back-end database. Follow the procedures described on this page to create and use data services.
Architect a Service Oriented Application
There are three basic steps to building an SOA-based application:
- Create a Service Provider Model that includes these Builders:
- An Integration Builder (SAP Function Call, SQL Call, Domino View, etc.) or method that provides data access
- A Service Definition Builder that creates underlying support for the service and names the service
- A Service Operation Builder(s) that invokes the data access support available in the Model
- Test service operation
Add a Service Test Builder to the Service Provider model (or enable the Test checkbox in Service Definition Builder).
- Create a Service Consumer model that includes these Builders:
- Service Consumer Builder that invokes service operations made available by a Service Provider Model
- Various Page Builders (View & Form, Record List & Details, etc.) to format and display data
(Optional) Generate a Stub Service Model in Service Provider Model by adding a Service Stub Builder
(Optional) Generate service documentation in either or both Models by adding Service Documentation Builders to the models
(Optional) Use the Service Mapping Registry to use the Stub Service Model or to switch between service implementations. To do so set service mappings in XML files located in: WEB-INF/config/service_mappings
Test a service provider?
Place a Service Test Builder in a model that contains the data service you want to test. This model must contain a Service Definition Builder and a Service Operation Builder that define the data service.
Configure the Service Test Builder by selecting the data service and enabling generation of a main method.
Run the model and, if necessary, provide any inputs required by the service on the Inputs page. View service output on the Results page to determine if the service returns the expected values.
To view information about the service being tested, enable the "Include Documentation" input in the Service Test Builder and run the model again. Information about the service and its operations will be displayed on the Index page.
Work disconnected from back-end data?
Add a Service Stub Builder call to the model you want to test. This model must contain a Service Definition Builder and a Service Operation Builder that define the data service.
In the Service Stub Builder choose the public data service name. Select a new model name for the stub service model, and save the Builder call.
Add a Service Test Builder call and provide any necessary default inputs. Save the Service Test Builder call and the model.
Refresh your project and you should see a newly-created model that implements a fully functional stubbed-out version of the original service.
Document a data service?
Add a Service Documentation Builder call to a service provider model. This model must contain a Service Definition Builder and a Service Operation Builder that define the data service.
In the Service Documentation Builder select "This model" for the "Model Selection" input, and "Services implemented" for the "Report Type".
Run the model to generate a page that displays information on the selected services, their operations and parameters.
Create an operation based on an integration Builder?
In a model, place a Service Definition Builder identifying the service that will hold the operation. Also place in the model an integration Builder (SAP Function Call, SQL Call, Web Service Call, or Domino View) that can successfully access back-end data.
Next add a Service Operation Builder to the model and, for "Operation Name," enter the name you want to use for your operation. A typical name might look like: findEmployees. In the Service Operation Builder, for "Action To Call" input, look under Data Services for the back end operation. For example, for a SQL Call, this might be something like: employeeSearch/execute.
We can configure the inputs (if any) and results of your operation. To use the same input and result structure as the back end operation you are calling, select "Use Structure From Called Action" for both inputs and results, and leave the Field Mapping choices on "Automatic". See below for information on transforming to a different schema structure.
You must use different techniques when working with Data Services and Methods. For example, Factory integration Builders create Data Service Operations and Methods to support their operations in the WebApp. In the "Action To Call" input, we can select either one, by picking under Data Services or Methods. However, as a general rule, you should select the Data Service Operation rather than the equivalent Method. When you select an operation from Data Services, the Service Operation Builder can access information about the input and output parameters of the operations, including the schema type information. When you select from Methods, specify the schema type for XML inputs or results.
Use an Action List, Method, or LJO method in an operation?
Place a Service Definition Builder in a model to hold the operation you want to add. The model must also contain the desired Action List, Method, or LJO you want to use. The Service Operation Builder supports the following types of method arguments and return values:
- Method Arguments - Arguments can be empty, a single IXml argument, or multiple simple types (String, int/Integer, double/Double, etc.) You cannot, for example, have multiple IXml arguments, or any complex Java object arguments, or a combination of IXml and simple type arguments.
- Return Values - Return can be void (no results) or IXml.
For XML inputs or results, have a schema in the WebApp that defines the structure you want. The schema can be created with a Builder such as Schema or Simple Schema Generator, or we can use any schema in the WebApp that was created by any other Builder.
Add a Service Operation Builder to the model and for "Operation Name" enter the name you want to use for your operation. For "Action To Call", look under Methods and select the action to call.
Next, configure your operation's inputs. You will have three possible choices:
- Method has no arguments - Select "No inputs" for Input Structure Handling.
- Method has a single IXml argument - in the Input Schema field, provide a schema type for the data. You can reference any schema in the WebApp.
- Method has multiple simple arguments - Select either "Use structure from called action", which will generate a schema from the arguments, or "Specify input schema", which lets you map your new operation inputs to the called method s inputs.
If your operation has results, specify the schema structure, in the Result Schema field. You can reference any schema in the WebApp.
Transform data returned by a service?
We can transform operation inputs and results into a different schema structure using this Service Operation Builder. To do this, make sure the desired schema is available in the WebApp. This schema can be created with a Builder such as Schema or Simple Schema Generator, or you can use any schema in the WebApp that was created by any other Builder.
When you call a service operation via the Service Consumer Builder, the result IXml data for the operation is a reference to the same IXml object specified in the provider's Service Operation Builder. It is not a separate copy of the data. This means that if the provider code keeps a reference to the IXml object, and if the result data is then modified in the Consumer model, the data will be changed in both places.
- Transform inputs - Select "Specify input schema" in the field for Input Structure Handling. Select your schema type in the Input Schema field. Then select "Specify input values" for Input Field Mapping. This gives you the Input Field Values table.
Input fields for the called operation are listed in the name column. You specify values for an input in the values column. If you open the value picker, all the fields from your operation s inputs are listed in the "Arguments" section. This makes it easy to map between fields if the two structures have different field names. You can also specify any other value to supply to the called action. For example, you may have some fields that have hard-coded values, or values from some Variable.
- Transform Results - Use the same approach for result transformation as you did for input transformation. Select "Specify result schema" in the field for Result Structure Handling. Select a schema type in the Result Schema field. Then select "Specify result values" for Input Field Mapping. This gives you the Result Field Values table.
Result fields from your new operation are listed the name column. You specify values for a result in the values column. If you open the value picker, all the fields from the results of the action you are calling are listed in the "Results" section. This makes it easy to map between fields if the two structures have different field names. We can also specify any other value to supply to the result field. For example, you may want to call a method to calculate a field value by picking from the MethodCall section.
Add custom input or result processing to an operation?
To perform additional manipulation or validation of data, we can call Java code during operation execution. For example, you might need to convert Date fields from a format used by back-end service, or you might want to have special handling for empty inputs to the service operation. Use the Additional Processing inputs of this Builder to accomplish both tasks.
There are two points during execution where we can invoke processing:
- Pre-Execute - Immediately before the called action is invoked
- Post-Execute - Immediately before the service operation results are returned.
In each case we can kick off processing by either firing an event or by calling an action. Here's how to use an event:
Call one of two events to initiate processing: <operationname>PreExecute and <operationname>PostExecute. Each event takes two IXml arguments. PreExecute event arguments are operationInputs, which holds the inputs passed to your operation, and calledActionInputs, holding the inputs to the action you are calling, after any transformations have been performed as specified in the Input Field Value table. PostExecute arguments are calledActionResults, which holds the results from the action you call, and operationResults, holding the results to your operation, after any transformations have been performed as specified in the Result Field Values table.
The SimpleServiceProvider sample model mentioned above provides an example of additional Pre-Execute processing. It checks input fields for null and changes them to "%" to use as a wildcard in SQL statement.
Use a Service Mapping Registry file?
One of the greatest benefits of using a Service Oriented Architecture (SOA) approach to building data-driven applications is that the data services code and the application using the data are completely separate. This separation allows the Service Provider model to be swapped for a different one. This swapping of Service Providers can be accomplished by any of the following methods.
- Manually changing the service Provider model reference in the Service Consumer builder call in the application (Service Consumer) model.
- Profiling the Service Provider reference in the Service Consumer builder call in the application model and then applying various values via various profiles to the get correct Service Provider model to be applied.
- Declaring selection and mapping rules for swapping Service Provider models in Service Mapping Registry files.
Using a Service Mapping Registry file has particular advantages over manual or profiled changes to the Service Provider references in the Service Consumer builder call.
- A Service Mapping Registry file can change the Service Provider at run-time and regen-time while the other methods only apply to regen-time.
- Service Mapping Registry files can be configured to effect many or even all Service Provider / Service Consumer relationships in a given web application project.
- Service Mapping Registry files can be configured to swap Service Providers based upon simple conditions such as pattern matching with the filenames of the Service Provider models or the value of a mapping parameter declared in the Service Consumer builder call.
Redirecting Service Provider references
Essentially, a Service Mapping Registry file works by identifying conditions under which a Service Provider reference need to be replaced by or swapped with another Service Provider reference and providing the model ID of the replacement Service Provider. For example, a Service Consumer builder call might refer to a Service Provider model named services\myDataServices.model in the WEB-INF\models folder in the web application project. A second Service Provider model named services\myDataServices_stub.model , built as a stub by using the stub generating features in the Service Definition builder call of the first Service Provider model, could exist in the project and be referenced by the Service Provider model.
For the convenience of the portlet developer, a Service Mapping Registry file could be configured so that at regeneration time, the stub provider is always used but at runtime, the real Service Provider, which relies on live data, would be used. To set up this configuration, the following XML instructions must exist in an XML Service Mapping Registry file stored in the web application project in the WEB-INF\config\service_mappings folder.
<ForService name="services/myDataServices" when="regen-time">
<UseStub />
</ForService>
In this case, the Service Consumer builder would reference the services/myDataServices model as the Service Provider. At regen-time, the Factory would know to swap out the myDataServices Service Provider model and use the stub model referenced in the myDataServices model's Service Definition builder call.
Registry Mapping File Selectors
There are several key constructs available in a Service Mapping Registry file. These constructs are divided into categories of Selectors and Mappings. Selectors provide the conditions under which Service Providers should be swapped with other Service Providers. The Mappings provide the names of the replacement Service Provider models.
Selectors evaluate to either true or false. If the conditions exist that make a Selector's criteria evaluate to true, then the Mapping declared inside the Selector is used. Consider the previous example:
<ForService name="services/myDataServices" when="regen-time">
<UseStub />
</ForService>
The ForService Selector accepts an attribute called name. If the value of the name attribute matches the model ID of the Service Provider referenced in the Service Consumer model, then the mapping gets applied. The model ID is derived from the full path and filename of the Service Provider model. A full path and filename of a Service Provider model looks like this: WEB-INF\models\subfolders\...\filename.model. The model ID omits the leading WEB-INF\models\ and the .model at the end of the filename. It also reverses the slashes. Therefore, a model located at WEB-INF\models\services\myDataServices.model has a model ID of services/myDataServices.
There are other Selectors that can be used in Service Mapping Registry files.
- ForAllServices - always evaluates to true unless additional attributes are supplied. For example,
<ForAllServices when="regen-time">
<UseStub />
</ForAllServices>
evaluates to true at regeneration time but false at runtime.
- ForServiceMatching - uses a regular expression (RegEx) pattern matching value in an attribute named pattern. Service Providers whose model IDs match the pattern get replaced by the Service Providers declared in the mapping portion of the Selector. For example,
<ForServiceMatching pattern="_testing">
<ModifyName pattern="_testing" replacement="_production" />
</ForServiceMatching>
Assuming a naming convention of Service Provider models that includes the substrings "_testing" and "_production", this construct would swap all Service Providers whose model IDs contained the substring "_testing" and replace them with the substring "_production". The actual models would not be changed, but any attempt to use a "_testing" Service Provider would cause the corresponding "_production" Service Provider to be used instead.
- IfParameterEquals - relies on Mapping Parameters declared in the Service Consumer builder call of the application model. This construct designates which Service Provider to use when certain name-value pairs exist in the Service Consumer model. For example, the construct below states for any attempt to use the services/myDataServices provider model, check to see if there is a Mapping Parameter called mode having a value of testing or production and swap the Service Provider accordingly. If no Mapping Parameter exists or if the value of the parameter doesn't match either value, then the services/myDataServices Service Provider model should be used.
<ForService name="services/myDataServices">
<IfParameterEquals name="mode" value="testing">
<UseService name="services/myDataServices_testing" />
</IfParameterEquals>
<IfParameterEquals name="mode" value="production">
<UseService name="services/myDataServices_production" />
</IfParameterEquals>
<UseService name="services/myDataServices" />
</ForService>
Registry Mapping File Mappings
By studying the examples given above, the defintions of the Mappings are fairly easy to understand.
- UseService - Use the named Service Provider model. If the named Service Provider does not exist, the Service Consumer model will generate an error.
- UseStub - Use the stub model referenced in the Service Provider model's Service Definition builder call. If there is no reference in that builder call, the original Service Provider is used. If there is a reference but the referenced stub model does not exist, the Service Consumer model will generate an error.
- ModifyName - Use the Service Provider whose model ID matches the resultant model ID once the declared pattern and replacement attributes are applied. For example, in the following construct, the Selector uses pattern matching to identify which Service Provider models should be swapped and the <ModifyName> mapping declares the replacment names. In this case, any attempt to use a Service Provider containing the substring "_testing" in its model ID will cause a Service Provider with the substring "_production" to be used instead. services/myDataServices_testing would be replaced by services/myDataServices_production.
<ForServiceMatching pattern="_testing">
<ModifyName pattern="_testing" replacement="_production" />
</ForServiceMatching>
More examples of allowable constructs are found in the following file: WEB-INF\config\service_mappings\mappings.xml.example