How to use UserService API

How to use UserService API

book

Article ID: KB0073313

calendar_today

Updated On:

Products Versions
TIBCO EBX All versions from 5.8.0.

Description

This article gives an example of a user service using the new UserService API implemented in EBX 5.8.0.

This sample user service's is a user service at record level, when it is launched on a record selection it will :

  • Create a new child dataspace.
  • Display a form to edit the selected record in the child dataspace.
  • After saving the record, merge the child dataspace to its parent.
  • Redirect the user to where he comes from.
You will find attached to this article the Java sources of the user service as well as the data model used in this sample.

Issue/Introduction

How to use UserService API

Resolution

User service declaration

This sample is a user service at record level, so the class EditRecord has to implement UserService<RecordEntitySelection>.

Cf.doc : https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/user_interface/user_services_implement.html#id1

public class EditRecord implements UserService<RecordEntitySelection>


For the same reasons, the class EditRecordserviceDeclaration has to implement UserServiceDeclaration.OnRecord.

Cf.doc : https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/user_interface/user_services_declare.html#id1

public class EditRecordserviceDeclaration implements UserServiceDeclaration.OnRecord


Here is the content of EditRecordserviceDeclaration, for each method, the documentation details the purpose and the lifecycle.

public class EditRecordserviceDeclaration implements UserServiceDeclaration.OnRecord{
 
    //Creates a new user service implementation.
    //This method is invoked every time the declared user service must be launched, once all activation controls have been performed.
    public UserService<RecordEntitySelection> createUserService() {
            return new EditRecord();
    }
     
    //Returns the identifier of the user service.
    //This method is invoked only once, when the user service is registered.
    public ServiceKey getServiceKey() {
        return ServiceKey.forName("EditRecord");
    }  
 
    //Defines the activation scope and rules of the service, namely where and when it is available and can be executed.
    //This method is invoked only once, when the user service is registered.
    public void defineActivation(ActivationContextOnRecord aContext) {
        aContext.includeAllDatasets();
    }
 
    //Defines the properties of the user service, in particular its label and description.
    //This method is invoked only once, when the user service is registered.
    public void defineProperties(UserServicePropertiesDefinitionContext aContext) {
        aContext.setLabel("Edit record");
    }
 
    //Defines the web component properties of the user service
    //This method is invoked only once, when the user service is registered.
    //Does nothing in this sample.
    public void declareWebComponent(WebComponentDeclarationContext aContext) {
    }
}


Now that we have a UserService and a UserServiceDeclaration implementations, we need to attach the user service to a given data model. That's the purpose of the class MySchemaextension.

public class MySchemaextension implements SchemaExtensions{
 
    public void defineExtensions(SchemaExtensionsContext aContext) {
        //Register a user service.
        aContext.registerUserService(new EditRecordserviceDeclaration());
    }
}


We will need then to declare the SchemaExtension in our data model.

User-added image

After data model publication, I can see the user service in the datasets that implement my data model.

User Service implementation

Now that we know how to declare and register a user service, the next step will be the user service implementation.
The wole implementation is done in the class EditRecord.Java.

The first method being called when the user service is executed is setUpObjectContext.
This method can define objects or remove objects managed by the user service. It is called once for each HTTP request.
Objects are new concepts appeared with the new UserService API.

An object can be of several types :
  • A record,

  • A data set,

  • A new record not yet persisted,

  • A dynamic object.

In this example, the object we want to manipulate/modify is the record on which the user service is executed, in a child dataspace.
So, in this method, we will create the child dataspace and map our object to the record in the newly created dataspace.

public class EditRecord implements UserService<RecordEntitySelection>{
 
    private static AdaptationHome currentDataSpace;
    private static AdaptationHome childDataSpace;
    private static Adaptation currentRecord;
    private static Adaptation childRecord;
     
    private static final ObjectKey childRecordObjectKey = ObjectKey.forName("childRecord");
     
    ...
     
    // Initiate the instance's variables and map the object 'childRecord' to a record in a newly created dataspace.
    public void setupObjectContext(
            UserServiceSetupObjectContext<RecordEntitySelection> aContext,
            UserServiceObjectContextBuilder aBuilder) {
         
        if( aContext.isInitialDisplay() )
        {
            currentRecord = aContext.getEntitySelection().getRecord();
            currentDataSpace = currentRecord.getHome();
            childDataSpace = this.createChildDataspace(aContext.getSession());
            childRecord = childDataSpace.findAdaptationOrNull(currentRecord.getAdaptationName());
             
            //Map the object 'childrecord' to the record in the child dataspace
            aBuilder.registerRecordOrDataSet(childRecordObjectKey, childRecord);
        }
    }
 
    //Creates a child dataspace.
    private AdaptationHome createChildDataspace(Session aSession){
        Repository repository = currentDataSpace.getRepository();
        HomeCreationSpec homeCreationSpec = new HomeCreationSpec();
        homeCreationSpec.setParent(currentDataSpace);
        homeCreationSpec.setOwner(aSession.getUserReference());
        homeCreationSpec.setKey(HomeKeyUtils.generateBranchKey(repository));
        try {
            return repository.createHome(homeCreationSpec, aSession);
        catch (OperationException e) {
            e.printStackTrace();
        }
        return null;
    }
...


Now that the child dataspace has been created and the record in the child dataspace has been mapped to the object context, we can go to the next step which is the setupDisplay method.
This method configures the user service display. It can setup the header, the bottom buttons and the content callback.
In our sample, we will display a form with 2 form rows and 5 buttons : 'save', 'save and close', 'revert', 'close' and 'cancel'.
 

public class EditRecord implements UserService<RecordEntitySelection>{
...
    private static final Path idPath = Path.parse("id");
    private static final Path labelPath = Path.parse("label");
...
    //Display the form and buttons.
    public void setupDisplay(
            UserServiceSetupDisplayContext<RecordEntitySelection> aContext,
            UserServiceDisplayConfigurator aConfigurator) {
         
        aConfigurator.setContent(this::displayChildRecordForm);
         
        UIButtonSpecSubmit saveButtonSpec = aConfigurator.newSaveButton(this::processSave);
        UIButtonSpecSubmit saveAndCloseButtonSpec = aConfigurator.newSaveCloseButton(this::processSaveAndClose);
        UIButtonSpecNavigation revertButtonSpec= aConfigurator.newRevertButton();
        UIButtonSpecNavigation closeButtonSpec = aConfigurator.newActionButton("Close"this::processClose);
        UIButtonSpecNavigation cancelButtonSpec = aConfigurator.newActionButton("Cancel"this::processCancel);
         
        aConfigurator.setLeftButtons( saveButtonSpec , saveAndCloseButtonSpec , revertButtonSpec , closeButtonSpec , cancelButtonSpec );
    }
 
    //Display the fields for the record to edit.
    private void displayChildRecordForm(UserServicePaneContext aPaneContext,
            UserServicePaneWriter aWriter)
    {
        aWriter.setCurrentObject(childRecordObjectKey);
        aWriter.addFormRow(idPath);
        aWriter.addFormRow(labelPath);
    }
...


We now have our form with buttons but we still need to implement the UserServiceEvent callback for each specific button.
In other terms, this is the code which is executed when you click on one of those buttons.


In this specific case the process methods do nothing but returning a named UserServiceEventOutcome, those UserServiceEventOutcome will then been passed as parameters of the method processEventOutcome which is the next method to be executed.

    public class EditRecord implements UserService<RecordEntitySelection>{
...
    private final static String saveEventName = "save";
    private final static String saveAndCloseEventName = "saveAndClose";
    private final static String closeEventName = "close";
    private final static String cancelEventName = "cancel";
...
    //Save button action.
    private UserServiceEventOutcome processSave(UserServiceEventContext anEventContext)
    {
        return new UserServiceEventOutcome() {
            public String name() {
                return saveEventName;
            }
        };
    }
     
    //Save and close button action
    private UserServiceEventOutcome processSaveAndClose(UserServiceEventContext anEventContext)
    {
        return new UserServiceEventOutcome() {
            public String name() {
                return saveAndCloseEventName;
            }
        };
    }
     
    //Close button action
    private UserServiceEventOutcome processClose(UserServiceEventContext anEventContext)
    {
        return new UserServiceEventOutcome() {
            public String name() {
                return closeEventName;
            }
        };
    }
     
    //Cancel button action
    private UserServiceEventOutcome processCancel(UserServiceEventContext anEventContext)
    {
        return new UserServiceEventOutcome() {
            public String name() {
                return cancelEventName;
            }
        };
    }
...

In the method processEventOutcome, we will manage any kind of possible named events:
  • If the user clicked on 'save', we will save the child record.
  • If the user clicked on 'save and close' , we will save the child record, merge the child dataspace and terminate the user service.
  • If the user clicked on 'revert' , the revert is automatic and there is no need to do anything.
  • If the user clicked on 'close' , we will merge the dataspace without saving the record and terminate the service.
  • If the user clicked on 'cancel' , we will close the dataspace without saving the record and terminate the service.
//Process events.
public UserServiceEventOutcome processEventOutcome(
        UserServiceProcessEventOutcomeContext<RecordEntitySelection> aContext,
        UserServiceEventOutcome anEventOutcome) {
    if(anEventOutcome.name().equals(saveEventName))
    {
        //Will automatically save the changes performed on the 'childRecord' object.
        aContext.save(childRecordObjectKey);
    }
    if(anEventOutcome.name().equals(saveAndCloseEventName))
    {
        //Will automatically save the changes performed on the 'childRecord' object.
        aContext.save(childRecordObjectKey);
        mergeChildDataSpace(aContext.getSession());
        //Terminates the user service
        return UserServiceNext.nextClose();
    }
    if(anEventOutcome.name().equals(closeEventName))
    {
        mergeChildDataSpace(aContext.getSession());
        //Terminates the user service
        return UserServiceNext.nextClose();
    }
    if(anEventOutcome.name().equals(cancelEventName))
    {
        closeChildDataSpace(aContext.getSession());
        //Terminates the user service
        return UserServiceNext.nextClose();
    }
    return null;
}
 
//Merge the child dataspace.
private void mergeChildDataSpace(Session aSession)
{
    Procedure proc = new Procedure() {         
        public void execute(ProcedureContext aContext) throws Exception {
            aContext.doMergeToParent(childDataSpace);              
        }
    };
     
    ProgrammaticService programmaticService = ProgrammaticService.createForSession(aSession, currentDataSpace);
    programmaticService.execute(proc);
}
 
//Close the child dataspace.
private void closeChildDataSpace(Session aSession)
{
    Repository repository = currentDataSpace.getRepository();
    try {
        repository.closeHome(childDataSpace, aSession);
    catch (OperationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}


In cases when the method return null, there is no more event to process and the method "setUpObjectContext" is called again.

Additional Information

Documentation:

https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/user_interface/user_services_overview.html
https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/user_interface/user_services_quickstart.html
https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/user_interface/user_services_implement.html
https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/user_interface/user_services_declare.html

Java documentation:

https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/Java_API/com/orchestranetworks/userservice/UserService.html
https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/Java_API/com/orchestranetworks/userservice/declaration/UserServiceDeclaration.html
https://docs.tibco.com/pub/ebx/5.9.13/doc/html/en/Java_API/com/orchestranetworks/schema/SchemaExtensions.html

Attachments

How to use UserService API get_app
How to use UserService API get_app
How to use UserService API get_app