Advertisements

Archive for the 'BPM' Category

Using BPM Java API – WorkflowContext

 

Once the human task instance is created, we can perform several actions like:

  • Withdraw
  • Delegate
  • Update Task outcome
  • Add Attachment
  • Add Comments
  • Query Task details
  • etc..

If we observe these actions, we can guess that all users of the system should not be able to perform these actions and should be controlled through roles/privileges/permissions. So all these actions requires a user context to determine these permissions. BPM APIs accept this user context in form of WorkflowContext. WorkflowContext is a session object maintained by SOA server tied to an user.

How to create workflowcontext for a specific user?

Task Query service has authenticate method that accepts user credentials and returns a WorkflowContext as shown below.


public IWorkflowServiceClient getWfServiceClient()
 {
 IWorkflowServiceClient wfSvcClient = null;

wfSvcClient = WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT);
return wfSvcClient;
 }

public IWorkflowContext getUserWorkflowContext()
 {
 IWorkflowContext wfCtx = null;
 try
 {
 wfCtx = getWfServiceClient().getTaskQueryService().authenticate("weblogic", "weblogic1".toCharArray(), null);
 }
 catch(WorkflowException wex)
 {
 wex.printStackTrace();
 }

 return wfCtx; 
 }

How to create workflowcontext for a logged in user?

Same Task Query service is used to get workflowcontext for the logged in user as shown below. Make sure that your java/web application is security enabled.

 public IWorkflowContext getLoggedinUserWorkflowContext()
 {
 IWorkflowContext wfCtx = null;
 try
 {
 wfCtx = getWfServiceClient().getTaskQueryService().getWorkflowContextForAuthenticatedUser();
 }
 catch(WorkflowException wex)
 {
 wex.printStackTrace();
 }

 return wfCtx; 
 }

How to create workflowcontext on behalf of a user?

In some of the cases, password may not be available to get the user context but the task actions should be performed by this user. In those cases, we can create admin workflow context and use authenticateOnBehalfOf method of Task Query Service as shown below. Here the user weblogic is admin i.e. having BPMWorkflowAdmin role.

 public IWorkflowContext getOnbehalfOfUserWorkflowContext()
 {
 IWorkflowContext wfCtx = null;
 try
 {
 wfCtx = getWfServiceClient().getTaskQueryService().authenticate("weblogic", "weblogic1".toCharArray(), null);
 wfCtx = getWfServiceClient().getTaskQueryService().authenticateOnBehalfOf(wfCtx, "svgonugu");
 }
 catch(WorkflowException wex)
 {
 wex.printStackTrace();
 }
 return wfCtx; 
 }

How to assign BPMWorkflowAdmin role to an User?

  • Login to EM console and navigate to Weblogic Domain -> <<domainName>> -> Security -> Application Roles.
  • Search for BPMWorkflowAdmin role by choosing soa-infra as application stripe.

search.jpg

  • Click Edit to assign this role to an user and click Add. Note that weblogic user will have admin role by default so the screenshots presented here are only demo purpose.

add.jpg

  • Search for the user weblogic and click ok.

add1

add3.jpg

  • You will observe the following error when try to use authenticateOnBehalfOf method using the workflowcontext created using non-admin user.

ORABPEL-30509

exception.code:30509
exception.type: ERROR
exception.severity: 2
exception.name: Insufficient privileges to authenticate on behalf of another user.
exception.description: User siva cannot authenticate on behalf of user svgonugu without admin privil
eges.
exception.fix: Only users with admin privileges can authenticate on behalf of another user.

Do we have any timeout settings for WorkflowContext?

As mentioned earlier, Workflowcontext is a session object maintained by SOA/BPM server in heap memory. So creating more and more workflowcontexts might cause out of memory errors on the server. So whenever we are done with workflowcontext we should always destroy the context. Also BPM runtime has a default timeout of 60 min after which the workflowcontext object is destroyed. This timeout setting can be modified by navigating to Weblogic Domain -> <<domain name>> -> System MBean Browser -> Application Defined MBeans -> oracle.as.soainfra.config -> Workflow Config -> WorkflowServiceSesionTimeoutInMinutes as shown below.

timeout

If we use workflowcontext after timeout, we will get WorkflowException.

How to destroy WorkflowContext?


getWfServiceClient().getTaskQueryService().destroyWorkflowContext(wfCtx);

Advertisements

Using BPM Java API – wf_client_config.xml

In previous posts (post1 & post2), we have seen how to use SOAP and Remote clients to work with BPM Java APIs. One immediate problem we can see is using server urls in the code which makes deployment across environments difficult. In this post, we will see how to take care of this.

WorkflowServiceClientFactory class has different overloaded methods to get the client. When we don’t pass any properties like below, BPM run time will look for a file wf_client_config.xml in classpath of ear file.


private IWorkflowServiceClient getWfServiceClient()
{
IWorkflowServiceClient wfSvcClient = null;

wfSvcClient = WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT);
return wfSvcClient;
}

We can create wf_client_config.xml with the following contents having the server details and information pertaining to both SOAP and REMOTE clients. The file can be placed in a folder like conf under our project folder.


<?xml version="1.0" encoding="UTF-8"?>
<workflowServicesClientConfiguration xmlns="http://xmlns.oracle.com/bpel/services/client">
<server name="default" default="true">
<remoteClient>
<serverURL>t3://localhost:7005/soa-infra</serverURL>
<userName>weblogic</userName>
<password>weblogic1</password>
<initialContextFactory>weblogic.jndi.WLInitialContextFactory</initialContextFactory>
<participateInClientTransaction>false</participateInClientTransaction>
</remoteClient>
<soapClient>
<rootEndPointURL>http://localhost:7005</rootEndPointURL>
<identityPropagation mode="dynamic" type="saml">
<policy-references>
<policy-reference enabled="true" category="security"
uri="oracle/wss10_saml_token_client_policy"/>
</policy-references>
</identityPropagation>
</soapClient>
</server>
</workflowServicesClientConfiguration>

Include this file in the ear deployment profile using the below steps i.e. corresponding to ADF BC Service project. Create new file group APP-INF/classes as shown below.

app-inf

Add the contributor pointing to conf directory having wf_client_config.xml file.

app-inf1.jpg

Now filters section of APP-INF/classes group should be shown like below.

app-inf2.jpg

Deploy the ear file and re-test your webservice method.

During testing, if the following error is seen in the logs make sure that <?xml version = ‘1.0’ encoding = ‘UTF-8’?> is present as first line in wf_client_config.xml file.

[Exception [EclipseLink-25008] (Eclipse Persistence Services – 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor with default root element workflowServicesClientConfiguration was not found in the project]

Since the server details are externalized you can use tokens in wf_client_config.xml and replace them using actual server urls before deploying to specific environments.

 

Using BPM Java API – Creating Human Task (Contd..)

In the previous post, we used SOAP client to invoke BPM java APIs to create a human task. As i mentioned, we will not be able to test remote interface unless we create some java application and deploy to WLS. So in this post, i will create ADF BC Service project to demonstrate this and assume the readers have basic understanding on how to create ADF BC Services.

Following code snippet shows getting the remote client to invoke BPM java API. note the usage of URL.


//admin credentials
private static final String wlsUser = "weblogic";
private static final String wlsPassword = "weblogic1";

//soa-infra url
private static final String soaURL = "t3://localhost:7005/soa-infra";

private Map<CONNECTION_PROPERTY, String> getClientProp(String clientType)
{
Map<CONNECTION_PROPERTY, String> properties = new HashMap<CONNECTION_PROPERTY, String>();

if (WorkflowServiceClientFactory.REMOTE_CLIENT.equals(clientType))
{
properties.put(CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");

//soa-infra url
properties.put(CONNECTION_PROPERTY.EJB_PROVIDER_URL, soaURL);

//admin user
properties.put(CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL, wlsUser);

//admin pwd
properties.put(CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS, wlsPassword);
}
else if (WorkflowServiceClientFactory.SOAP_CLIENT.equals(clientType))
{
properties.put(CONNECTION_PROPERTY.SOAP_END_POINT_ROOT,
"http://localhost:7005");
properties.put(CONNECTION_PROPERTY.SOAP_IDENTITY_PROPAGATION, "non-saml");
}
return properties;
}

private IWorkflowServiceClient getWfServiceClient()
{
IWorkflowServiceClient wfSvcClient = null;

wfSvcClient =
WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT,getClientProp(WorkflowServiceClientFactory.REMOTE_CLIENT),null);
return wfSvcClient;
}

The AM method code which is exposed as webservice method remains similar as shown below.


private Element getTaskPayload() throws Exception
{
String payloadStr =
" <payload xmlns=\"http://xmlns.oracle.com/bpel/workflow/task\">" +
"<EmployeeExpenseInput xmlns=\"http://xmlns.oracle.com/expenses/approval/schema\">" +
" <employeeId></employeeId> " + " <firstName></firstName> " + " <lastName></lastName>" +
" <expenseType></expenseType>" + " <expenseDescription></expenseDescription>" +
" <expenseLocation></expenseLocation>" + " <expenseDate></expenseDate>" +
" <amount></amount>" + "</EmployeeExpenseInput>" + " </payload>";
Document doc = null;

try
{
doc = XMLUtil.parseDocumentFromXMLString(payloadStr);
}
catch (Exception e)
{
throw new Exception("Exception in parsing string to xml");
}
return doc.getDocumentElement();
}

public String createHumanTask(String taskTitle)
{
String taskId = null;
IInitiateTaskResponse taskResponse = null;

try
{
ObjectFactory of = new ObjectFactory();
Task newTask = of.createTask();

//set required attribute before calling BPM task api
newTask.setTaskDefinitionId(taskNameSpace);
newTask.setPayloadAsElement(getTaskPayload());
newTask.setCreator(wlsUser);
if(taskTitle == null || taskTitle.isEmpty())
{
newTask.setTitle("BPM API TESTING USING CREATE HUMAN TASK");
}
else
{
newTask.setTitle(taskTitle);
}

newTask.setCategory("TESTING");
newTask.setIdentificationKey("587776676");

taskResponse = getWfServiceClient().getTaskService().initiateTask(newTask);

if (taskResponse != null)
{
newTask = taskResponse.getTask();
taskId = newTask.getSystemAttributes().getTaskId();
}
}
catch (Exception e)
{
e.printStackTrace();
}
return taskId;
}

When the service method is invoked for the first time, we may observe the following error.


<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>JBO-29000: Unexpected exception caught: java.lang.NoClassDefFoundError, msg=oracle/bpel/services/workflow/task/model/ObjectFactory</faultstring>
<detail>
<tns:ServiceErrorMessage xmlns:tns="http://xmlns.oracle.com/adf/svc/errors/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tns:code>29000</tns:code>
<tns:message>JBO-29000: Unexpected exception caught: java.lang.NoClassDefFoundError, msg=oracle/bpel/services/workflow/task/model/ObjectFactory</tns:message>
<tns:severity>SEVERITY_ERROR</tns:severity>
<tns:detail xsi:type="tns:ServiceErrorMessage">
<tns:message>oracle/bpel/services/workflow/task/model/ObjectFactory</tns:message>
<tns:severity>SEVERITY_ERROR</tns:severity>
<tns:exceptionClassName>java.lang.NoClassDefFoundError</tns:exceptionClassName>
</tns:detail>
<tns:exceptionClassName>oracle.jbo.JboException</tns:exceptionClassName>
</tns:ServiceErrorMessage>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>

This error can be resolved by adding oracle.soa.workflow.wc as library reference in weblogic-application.xml. The file can be found in Application Resources -> Descriptors -> META-INF in JDeveloper.


	<library-ref>
	<library-name>oracle.soa.workflow.wc</library-name>
</library-ref>

Testing

Input

input

Output

output

output2

output3.jpg

The sample ADF BC service project can be found here.

Using BPM Java API – Creating Human Task

In the previous post, I described how different participant types work when we use Named User, Approval Group, Application Role etc in the assignment. Now we will see how to use BPM Java APIs in a series of post. I used 12.2.1.2.0 release for demonstration. This article assumes basic working knowledge of Human Workflows and does not get into finer details of the same.

SOA/BPM exposes number of human workflow services as listed below and all these services can be invoked either using SOAP or remote interface with an exception of Identity service which can be invoked only through SOAP interface. You can refer to url for more information on accessing the service and functionality exposed by each. In this post, we will see how to use Task Service to create a human task from java code.

  • Task service
  • Task query service
  • Identity service
  • Task metadata service
  • User metadata service
  • Task report service
  • Runtime config service
  • Evidence store service

Steps:

  • Create a Human Workflow in a SOA composite and deploy to SOA Server.
  • Create a java project in Jdeveloper with a java class having main method.
  • Add BPM Workflow library by navigating to Project properties -> Libraries and Classpath. This jar will have necessary classes to work with human workflow services mentioned above. Note that the libraries Oracle XML Parser V2 and JAX-WS client also need to be added.
  • To call any of the human workflow services, we need to get workflow service client as the first step which can either SOAP or REMOTE. Following snippet of code shows how to get a SOAP based workflow service client.
private IWorkflowServiceClient getWfServiceClient()
{
IWorkflowServiceClient wfSvcClient = null;
wfSvcClient =
WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.SOAP_CLIENT,
getClientProp(WorkflowServiceClientFactory.SOAP_CLIENT),null);
return wfSvcClient;
}

private Map<CONNECTION_PROPERTY, String> getClientProp(String clientType)
{
Map<CONNECTION_PROPERTY, String> properties = new HashMap<CONNECTION_PROPERTY, String>();
if (WorkflowServiceClientFactory.REMOTE_CLIENT.equals(clientType))
{
properties.put(CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");

//soa-infra url
properties.put(CONNECTION_PROPERTY.EJB_PROVIDER_URL, soaInfraURL);

//admin use
properties.put(CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL, wlsUser);

//admin pwd
properties.put(CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS, wlsPassword);
}
else if (WorkflowServiceClientFactory.SOAP_CLIENT.equals(clientType))
{
properties.put(CONNECTION_PROPERTY.SOAP_END_POINT_ROOT, "http://host:soaPort");
properties.put(CONNECTION_PROPERTY.SOAP_IDENTITY_PROPAGATION, "non-saml");
}
return properties;
}
  • Use the following code to create a task using Task service. You can get task namespace from .task file and is the targetNamespace mentioned there. The Task payload preparation should be as per payload defined in Data section of the human task. Note the usage of workflow service client to get the task service.

private Element getTaskPayload()
throws Exception
{
String payloadStr =
" <payload xmlns=\"http://xmlns.oracle.com/bpel/workflow/task\">" +
"<EmployeeExpenseInput xmlns=\"http://xmlns.oracle.com/expenses/approval/schema\">" +
" <employeeId></employeeId> " + " <firstName></firstName> " + " <lastName></lastName>" +
" <expenseType></expenseType>" + " <expenseDescription></expenseDescription>" +
" <expenseLocation></expenseLocation>" + " <expenseDate></expenseDate>" +
" <amount></amount>" + "</EmployeeExpenseInput>" + " </payload>";
Document doc = null;

try
{
doc = XMLUtil.parseDocumentFromXMLString(payloadStr);
}
catch (Exception e)
{
throw new Exception("Exception in parsing string to xml");
}
return doc.getDocumentElement();
}

public String createHumanTask()
{
String taskId = null;
IInitiateTaskResponse taskResponse = null;

try
{
ObjectFactory of = new ObjectFactory();
Task newTask = of.createTask();

//set required attribute before calling BPM task api
newTask.setTaskDefinitionId(taskNameSpace);
newTask.setPayloadAsElement(getTaskPayload());
newTask.setCreator(wlsUser);
newTask.setTitle("BPM API TESTING USING CREATE HUMAN TASK");
newTask.setCategory("TESTING");
newTask.setIdentificationKey("587776676");

taskResponse = getWfServiceClient().getTaskService().initiateTask(newTask);

if (taskResponse != null)
{
newTask = taskResponse.getTask();
taskId = newTask.getSystemAttributes().getTaskId();
}
}
catch (Exception e)
{
e.printStackTrace();
}
return taskId;
}

  • Similarly, other human workflow services can be accessed using workflow service client. Note that you may not be able to test the remote client from java main method. You need to deploy this to WLS to see it in action.
  • You can download the java project from here.

Advertisements

Pages

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 341 other followers

Enter your email address to follow this blog and receive notifications of new posts by email.