Customizing Workflows
In CloudPortal™ Business Manager, user can perform certain actions to achieve a desired business purpose. These actions can be considered as business transactions that help in achieving this business purpose. A workflow is a multi-stage process that consists of multiple phases. Every phase consists of a set of activities, (smallest unit of work in a workflow) that should be completed successfully in order to complete the phase. All phases should be completed to complete a business transaction.
- Move the activities into another phase.
- Change the order of the phases.
- Remove the activities from any phase. Note: The above mentioned tasks can be edited in workflows.xml
- Add any new activity to any phase.
- Add a new phase with new activities or existing activities.
To perform the above mentioned tasks, follow the steps explained below:
- Create an activity in citrix.cpbm.custom bundle by creating a new class which implements the interface "com.citrix.cpbm.workflow.activity.Activity".
- Create a task within the activity, so that the task will be opened when the activity is started. Consider the following example where AddSecondaryEmailActivity is the activity being created, and ADD_SEC_EMAIL is the task within that activity and its status is being checked for completion.
package com.citrix.cpbm.workflow.custom.activity; import com.citrix.cpbm.core.workflow.model.BusinessTransaction; import com.citrix.cpbm.core.workflow.model.Task; import com.citrix.cpbm.core.workflow.model.Task.State; import com.citrix.cpbm.workflow.activity.Activity; import com.citrix.cpbm.workflow.activity.WorkflowActivityExecutionException; import com.vmops.service.AuthorityService; import com.vmops.service.UserAlertPreferencesService; public class AddSecondaryEmailActivity implements Activity{ private AuthorityService authorityService; private UserAlertPreferencesService userAlertPreferencesService; public void setUserAlertPreferencesService( UserAlertPreferencesService userAlertPreferencesService) { this.userAlertPreferencesService = userAlertPreferencesService; } public AuthorityService getAuthorityService() { return authorityService; } public void setAuthorityService(AuthorityService authorityService) { this.authorityService = authorityService; } @Override public void start(BusinessTransaction businessTransaction, Record activityRecord) throws WorkflowActivityExecutionException { } @Override public Status run(BusinessTransaction businessTransaction, Record activityRecord) throws WorkflowActivityExecutionException { Status activityStatus; if (isActivityComplete(businessTransaction, activityRecord)) { activityStatus = Status.SUCCESS; } else { activityStatus = doActivity(businessTransaction, activityRecord); } if (activityRecord.getTask() == null && Status.WAITING == activityStatus) { Task action = createAction(businessTransaction); activityRecord.setTask(action); } return activityStatus; } protected Boolean isActivityComplete(BusinessTransaction businessTransaction, Record record) throws WorkflowActivityExecutionException { if(userAlertPreferencesService.getCount(businessTransaction.getTenant().getOwner())>0){ return true; }else return false; } public Task createAction(BusinessTransaction businessTransaction) throws WorkflowActivityExecutionException { Task action = new Task("ADD_SEC_EMAIL", businessTransaction.getTenant(), businessTransaction.getTenant().getOwner(), businessTransaction, State.PENDING, authorityService.findByAuthority("ROLE_ACCOUNT_USER_CRUD"), Task.DisplayMode.POPUP); return action; } protected Status doActivity(BusinessTransaction businessTransaction, Record activityPayload) throws WorkflowActivityExecutionException { Task action = activityPayload.getTask(); if (action != null) { if (action.getState() == Task.State.SUCCESS) { return Status.SUCCESS; } else if (action.getState() == Task.State.FAILURE) { return Status.FAILURE; } } return Status.WAITING; } }
- Add the newly created custom activity bean in applicationContext-workflow-customizations.xml file which is in src/main/resources/META-INF/spring/ of citrix.cpbm.custom.common bundle. For the above example, following bean is added:
<bean id="addSecondaryEmailActivity" class="com.citrix.cpbm.workflow.custom.activity.AddSecondaryEmailActivity"> <property name="authorityService" ref="authorityService"/> <property name="userService" ref="userService"/> <property name="userAlertPreferencesService" ref="userAlertPreferencesService"/> </bean>
- Add the activity reference in osgi-context.xml file which is in src/main/resources/META-INF/spring/ of citrix.cpbm.custom bundle. For the above example, following reference is added:
<osgi:service interface="com.citrix.cpbm.workflow.activity.Activity" ref="addSecondaryEmailActivity" />
Note: Also publish new service dependencies (if any). - Specify a user friendly name for the task label in CustomApplicationResources.properties file in src/main/resources/OSGI-INF/l10n/resources of citrix.cpbm.custom bundle for it to be displayed in the UI task list.
For the above example, following labels are specified to display when secondary email task is created
ui.widget.task.type.ADD_SEC_EMAIL.name= Add Secondary Email for {0}
ui.task.type.ADD_SEC_EMAIL.name= Add Secondary Email for {0}
- Add a new rule in transactionWorkflowMap.xml.
To write rules in transaction workflow map (transactionWorkflowMap.xml):
Whenever a business transaction is triggered, the system will apply the applicable rules available in transactionWorkflowMap.xml on the business transaction and if the rule matches, then it will return the corresponding workflow. If multiple rules gets matched, then the system will return the workflow associated with a match that comes first.
For example -<Match type="tenantStateChange" workflow="credit-card-account-activation"> <Rule>{#transaction.tenantInitialState.name == 'NEW' and #transaction.tenantTargetState.name == 'ACTIVE' and #transaction.tenant.accountType.paymentModes == 2}</Rule> </Match>
In above snippet, when a business transaction of type tenantStateChange is triggered for initial state "NEW" and target state "ACTIVE" for a tenant having payment mode as 2 (which internally means credit card), then system returns the credit-card-account-activation workflow.
Writing a rule
For writing a rule, any direct or transitive property available in business transaction object can be used.
Following are the properties available directly or transitively in different business transactions:
TenantStateChangeTransactionName | Type |
tenant | com.vmops.model. Tenant |
tenantInitialState | com.vmops.model.Tenant.State |
tenantTargetState | com.vmops.model.Tenant.State |
TenantAccountTypeConversionTransaction
Name | Type |
tenant | com.vmops.model. Tenant |
accountTypeInitial | com.vmops.model.AccountType |
accountType | com.vmops.model.AccountType.State |
paymentInfo | String |
PaymentInfoChangeTransaction
Name | Type |
tenant | com.vmops.model. Tenant |
accountTypeInitial | com.vmops.model.AccountType |
accountType | com.vmops.model.AccountType.State |
paymentInfo | java.lang.String |
SubscriptionActivationTransaction
Name | Type |
tenant | com.vmops.model. Tenant |
subscription | com.vmops.model.Subscription |
subscriptionInitialState | com.vmops.model.Subscription.State |
subscriptionTargetState | com.vmops.model.Subscription.State |
isProvisioned | java.lang.String |
CloudServiceActivationTransaction
Name | Type |
serviceInstance | com.vmops.model.ServiceInstance |
initialState | com.vmops.model.TenantHandle.State |
targetState | com.vmops.model.TenantHandle.State |
instanceProperty | java.lang.String |
com.vmops.model.Tenant
Name | Type |
sourceChannel | com.vmops.model. Channel |
accountType | com.vmops.model.AccountType |
spendLimit | java.math.BigDecimal |
initialDeposit | java.math.BigDecimal |
address | com.vmops.model.Address |
currency | com.vmops.model.CurrencyValue |
state | com.vmops.model. Tenant.State |
createdAt | java.util.Date |
Subscription
Name | Type |
state | com.vmops.model. Subscription.State |
tenant | com.vmops.model.Tenant |
productBundle | com.vmops.model.ProductBundle |
createdAt | java.util.Date |
derivedFrom | com.vmops.model.Subscription |
AccountType
Name | Type |
name | java.lang.String |
paymentModes | java.lang.Long |
trial | boolean |
paymentInfoRequired | boolean |
autoPayRequired | boolean |
instantpayRequired | boolean |
vextendCredit | boolean |
selfRegistrationAllowed | boolean |
manualRegistrationAllowed | boolean |
notionalBilling | boolean |
manualActivation | boolean |
Channel
Name | Type |
name | java.lang. String |
code | java.lang. String |
ProductBundle
Name | Type |
name | java.lang. String |
code | java.lang. String |
com.vmops.model.CurrencyValue
Name | Type |
currencyCode | java.lang. String |
currencyName | java.lang. String |
com.vmops.model.Address
Name | Type |
city | java.lang. String |
state | java.lang. String |
postalCode | java.lang. String |
country | java.lang. String |
Service
Name | Type |
Category | java.lang.String |
type | java.lang.String |
serviceName | java.lang.String |
vendor | java.lang.String |
adapterRef | java.lang.String |
ServiceInstance
Name | Type |
service | com.vmops.model.Service |
name | java.lang.String |
description | java.lang.String |
code | java.lang.String |
Creating Custom Action URL Resolver
If the Task created Type is NAVIGATE, then you have to specify the action Resolver URL. Following are the steps to create custom action URL resolver:- Create a CustomActionUrlResolver.java and populate the parameters list. For Example : Below is the sample code of creating the customActionUrlResolver for above created adding secondary email activity.
/* Copyright (C) 2012 Citrix, Inc. All rights reserved. */ package com.citrix.cpbm.workflow.custom.resolver; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.citrix.cpbm.core.workflow.model.Task; import com.citrix.cpbm.workflow.pendingaction.resolver.impl.AbstractActionUrlResolver; public class CustomActionUrlResolver extends AbstractActionUrlResolver { private Map<String, String> actionUrls = new HashMap<String, String>(); @Override public Set<String> getSupportedTypes() { return actionUrls.keySet(); } public void setActionUrls(Map<String, String> actionUrls) { this.actionUrls = actionUrls; } @Override public List<String> getParams(Task action) { List<String> params = new ArrayList<String>(); if ("ADD_SEC_EMAIL".equalsIgnoreCase(action.getType())) { params.add(action.getTenant().getOwner().getUuid()); } return params; } @Override public String getActionUrl(Task action) { return actionUrls.get(action.getType()); } }
- Add the bean for the newly created CustomActionUrlResolver in applicationContext-workflow-customizations.xml file which is under src/main/resources/META-INF/spring/ of citrix.cpbm.custom bundle. Provide the target URL in the value field of <entry> For the above example, following bean is added for newly created sample CustomActionUrlResolver.
<bean id="customActionUrlResolver" class="com.citrix.cpbm.workflow.custom.resolver.CustomActionUrlResolver"> <property name="actionUrls"> <map> <entry key="ADD_SEC_EMAIL" value="users/%1$s/myprofile"></entry> </map> </property> </bean>
-
Add the reference of the newly created CustomActionUrlResolver in osgi-context.xml file which is under src/main/resources/META-INF/spring/ of citrix.cpbm.custom bundle.
For the above example, following reference is added:<osgi:service interface="com.citrix.cpbm.workflow.pendingaction.resolver.ActionUrlResolver" ref="customActionUrlResolver"/>
Comments