Products | Versions |
---|---|
TIBCO EBX | All versions from 5.8.0. |
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 :
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.
After data model publication, I can see the user service in the datasets that implement my data model.
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.
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; } }; } ... |
//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.