Wednesday, May 9, 2012

Querying BPEL Process Instances

The Oracle BPEL Console provides a great tool for monitoring the status of in-flight and completed process. However it is often a requirement to present this information to a business user within a business specific view. One way of achieving this is to use the Oracle BPEL Portlets. This is fine where you want to give the user a specific list of process instances, for example a list of all currently running Purchase Order processes. But what about when you need to give them a list more filtered to their specific business requirements, for example: How do I find all open purchase order processes for a specific customer? How do I find all help desk processes being managed by a specific customer service representative? How do I find all open expense processes that are waiting approval? In addition once you’ve located a process instance, how do I provide the user with access to relevant data contained within the process? Again the BPEL Console provides a mechanism for drilling into the process and looking at the audit trail for the data. But often we want to provide this data in a summarised business view designed specifically for the needs of the business user. The Oracle BPEL PM Server provides a series of API’s that enable you to meet these requirements. In fact what is not often realised is that the BPEL Console itself makes use of these API’s, giving you the flexibility to completely re-write the console if that is what’s required!!! In reality this is rarely if ever the case, typically the requirement is to provide business users with a simplified view tailored to their specific needs. A simple way of achieving this is through the use of these API’s; this is the subject of this article. Locating a Process Instance The Oracle BPEL PM Client API provides a Locator class for enabling a client application to search for processes, instances and activities. The Locator class provides a number of constructors, which enable you to connect to a BPEL domain hosted on either a local or remote J2EE Server. For the purpose of locating specific process instances it provides two very useful methods: listInstances(WhereCondition wc) listInstancesByIndex(WhereCondition wc) Each method returns an array of objects of type InstanceHandle, which can then be used to perform operations on the corresponding process instance. The key parameter for each of these methods is the WhereCondition, which is used to build up a query to restrict which instances are returned by the method. Note: Where condition objects may be concatenated together to form a larger query. The methods append and prepend allow the user to add a clause (in String format) or even a whole WhereCondition object to the beginning or end of the current where condition. The following code snippet shows how to construct a WhereCondition to return all running process instances for the “LoanFlowProcess” where it’s current status is “CheckingCredit”. String pProcessId = "LoanFlowProcess"; String pStatus = "CheckingCredit"; // Constructs a where condition that searches for open instances WhereCondition where = WhereConditionHelper.whereInstancesOpen(); // Extend the where condition to filter on process id WhereCondition whereProcessId = new WhereCondition( "process_id = ?" ); whereProcessId.setString(1, pProcessId); where.append(whereProcessId); // Extend the condition to filter on processes with the status ‘CheckingCredit’ WhereCondition whereStatus = new WhereCondition( "status = ‘" + pStatus + " ); whereStatus.setString(1, pStatus); where.append("and").append(whereStatus) // Find Instances IInstanceHandle[] instanceHandles = locator.listInstances( where ); In the final step, the actual locator class is performing a query against the BPEL Dehydration store, similar to the one illustrated below: “select cikey from cube_instance where " + whereCondition.getClause(); We use the WhereCondition (which wraps a SQL prepared statement where condition), in order to restrict the result set returned by the query. It’s worth exploring in a bit more detail the various parts of the WhereCondition. For the first we use the WhereConditionHelper class to restrict the query to only currently running processes. This is a simple utility class which provides a variety of Static methods for creating various query fragments (e.g. return process instance whose state is open, completed, aborted, stale, etc) which can then be appended to additional where conditions to create the required query. For our second condition we are literally adding the condition cube_instance.process_id = “LoanFlowProcess” to our prepared statement. Here you can specify pretty much any of the columns in the database table cube_instance (e.g. Process_Id, Revision_Tag, Priority, Status). In reality, rather than naming this column explicitly, we should use the appropriate constants defined in com.oracle.bpel.client.util.SQLDefs (e.g. SQLDefs.CI_process_id for our example). The final statement is similar to the second in that we are querying on the process status. But what is the process status? Well it shouldn’t be confused with process state, which we queried on in the first WhereCondition. Rather the process status is a variable that keeps track of where in the process a particular process instance is. When the process is first initiated, this value is set to ‘initiated’. This value is then updated every time you enter a new scope within a process to contain the name of the scope. Note: When you have nested scopes, it will contain the name of the lower most nested scope that the process is in, i.e. it contains the last scope that was entered. Also when a process leaves a scope the status value is NOT reset, i.e. it will still contain the name of the previous scope until it enters a new scope. Process Indexes The listInstances method is very useful but it still doesn’t allow us to perform a query based on actual data held in the process instance, e.g. just return the loan flow process for ‘Dave’. To solve this problem, BPEL allows for a process to have up to 6 indexes and to create a where condition across one or more of these indexes. Essentially there are two steps to this; first you need to set the index values on the actual process instance; secondly you use the index values in a query to pull back all processes for a particular index value in a similar fashion to above. Setting the Index Value The simplest way to achieve this is to embed a piece of Java (using the Java Embed Task) at the start of the process to call the setIndex API to set the index value based on a value in the initial message, as shown in the example below: // Set Index1 for Customer Name String customerName = ((com.collaxa.cube.xml.dom.CubeDOMText) getVariableData("input", "payload", "/auto:loanApplication/auto:customerName/text()")).getText(); setIndex(1, customerName); Note: The getVariableData method is used to retrieve the customerName from the “input” variable; the syntax of the parameters is similar to the “from” component within an assign construct. Querying Processes The following code snippet shows how to construct a WhereCondition to return all running process instances where index_1 is equal to ‘Dave’. String pCustomerName = "Dave"; // Constructs a where condition that searches on index 1 WhereCondition where = new WhereCondition(SQLDefs.CX_index_1 + " = ?"); where.setString(1, pCustomerName); // Find Instances IInstanceHandle[] instanceHandles = locator.listInstancesByIndex( where ); However there is one minor issue with this; under the covers the listInstances method is performing a query on the cube_instance database table, whilst the listInstancesByIndex is performing a query on the ci_indexes database table. The issue here is if we want to perform a query that is a join across these two tables, i.e. show me all LoanFlow process for Dave. The WhereCondition API doesn’t (naturally) allow for joins; however there are two possible workarounds: The first is to set the index values to hold the additional data required by the query, e.g. set index_1 to hold the process name and index_2 to hold the customer id. The second is to extend the where condition passed to the listInstance method to have an IN condition that queries against the ci_indexes database table, as show below: // Extend the where condition to only return open processes with the WhereCondition whereIndex = new WhereCondition( "cikey in (select cikey from ci_indexes where index_1 = ?"); whereIndex.setString(1, pCustomerName); where.append("and").append(whereIndex) Note: I’ve used table and column names for clarity, but in reality you should use the constants defined by SQLDefs. Using an Index to set status Earlier in the article we looked ay how we can use process status to keep track of where we currently are in a process (remember status contains the name of the last scope that we entered). However, whilst this is useful it has a couple of drawbacks; one is that if we use the listInstancesByIndex method to locate a process, we can’t actually filter on the state of the process. However the other is that we rely on insuring that the scopes are correctly named, designed, etc to keep track of where we are in the process. However in reality we may only have a few key milestones that we are interested in, and these may span several scopes or we may have more than one milestone contained within the same scope. An alternative is to use an Index to hold the status of the process, and just update the status of the process using the setIndex method at appropriate points within the process. Accessing Process Data Once we have our list of processes instances, the final stage is to actually access the relevant data contained within the instance to display to the business user. This is simply the case of iterating through our array of instance handles. Once you have the instanceHandle for a process, you can then use this to access process variables contained within the process instance using the getField method. However you need to take care that whatever variable you are trying to access is currently visible within the active scope of the process. I find the simplest way to do this is to define a global variable (i.e. define the variable at the process level) and initialise it at the beginning of the process based upon the content of the initial message received by the process. Then during the lifetime of the process update the variable as required to reflect the true state of the process. The following code snippet shows how we can process each instance returned by the locator and access the variable “LoanApplicationSummary” defined in the BPEL process. // Find Instances IInstanceHandle[] instanceHandles = locator.listInstancesByIndex( where ); // Process each instance for (int i = 0; i < instanceHandles.length; i++ ) { IInstanceHandle instanceHandle = instanceHandles[ i ]; // Get Loan Application Summary Variable Element loanApplicationElement = (Element) instanceHandle.getField(“LoanApplicationSummary”); // Create Loan Application Bean LoanApplication loanApplication = LoanApplicationFactory.createFacade(loanApplicationElement); // Process Loan Application Bean as required … } To access the variable you use getField method on your instance Handle, as show below: // Get Loan Application Summary Variable Element loanApplicationElement = (Element) instanceHandle.getField(“LoanApplicationSummary”); This will return a DOM (Document Object Model) representing the XML contained within the process variable. You can use the actual DOM API to parse and manipulate the XML content but for any complex structure this can be quite tricky. To make this simpler, Oracle BPEL Process Manager provides a lightweight JAXB-like Java object model on top of XML; a so called XML façade. The façade provides a Java bean-like front end for an XML document/element. Façade classes have a corresponding Factory class which parse the XML document/element to create the façade, as show in the code snippet below: // Create Loan Application Bean LoanApplication loanApplication = LoanApplicationFactory.createFacade(loanApplicationElement); Once the façade has been created, you can use its getter methods to access the required data. Note: Façades are generated using the schemac tool shipped with Oracle BPEL Process Manager. You can use schemac to generate the façades from WSDL or XSD files (see the Oracle BPEL PM Developer guide for further details). Summary As we have seen Oracle BPEL PM provides a powerful Client API that makes it relatively simple to build a business specific “console” tailored to the needs of the user. For further information on the API you should see the Oracle BPEL Process Manager Client API Reference Posted by Matt Wright at 14:51 10 comments Wednesday, 14 February 2007 "Private" BPEL Processes When developing any BPEL based solution, good practice dictates that you take a modular approach to process design, which allows the sharing of sub-processes among higher level processes. For example a payment process may be used by both the Expenses Process and Order Process. As a result you will often end up with BPEL processes that you only intend to be called by other BPEL processes, and typically BPEL processes with at least some knowledge about the underlying process. So how do you prevent other ‘clients’ from directly invoking these processes? Now initially this may sound like a security issue, and Oracle BPEL Process Manager provides a number of ways of securing BPEL Processes; in addition Oracle Web Services Manager provides a comprehensive solution for adding policy-driven security to all Web services (not just BPEL Processes). However security is typically intended for enabling controlled secured access to a BPEL Process (or Web Service) by authorized clients. However in this case we don’t actually want any client directly accessing the process. Now admittedly we could take a standard based security approach to this, but is there a simpler way? Now many programming languages such as Java provide the concept of private or protected methods that control what has access to them. For example, in Java a class may declare some of its methods as being protected; indicating that only other classes in this package can access these methods. The great thing about this approach is that the developer is actually signalling a level of intent, i.e. this method should not be called directly except by related classes (or sub-classes) that can be trusted to use the method correctly. Ideally it would be great if BPEL provided similar functionality, however unfortunately it doesn’t. So is there a way of achieving something similar? Well it turns out there is a fairly straight forward way of getting close to the desired effect. The approach makes use of the Oracle HTTP Server embedded within the Oracle Application Server (as such this won’t work for the Developer install) to prevent access to a specific URL pattern, plus the use of domains with Oracle BPEL PM to enable us to create a simple URL pattern for all “private” processes. Configuring Oracle HTTP Server Oracle HTTP Server is the Web server component of Oracle Application Server and is based on the Apache infrastructure. Any one familiar with Apache administration is aware that it provides Allow and Deny directives which let you either allow or deny access to a particular URL (or pattern) based on the host name, IP address (or partial IP address) or a fully qualified domain name (or partial domain name). By specifying a parameter in the httpd.conf file we can create a directive to only allow access to a URL which matches the specified pattern from the host on which the Oracle Application Server itself is installed. For example the following directive will only allow access from the host to any URL which contains the string “/orabpel/private/”: Order deny, allow Deny from all Allow from localhost Note: The httpd.conf file can be found in the directory: /Apache/Apache/conf Configuring Oracle BPEL PM Now the trick is here to be able to provide a simple URL pattern on which to place the restriction. This is actually rather straight forward. Oracle BPEL PM has the concepts of domains into which a BPEL process is deployed. A BPEL domain allows a single instance of Oracle BPEL Process Manager to be partitioned into multiple virtual BPEL sections (each identified by an ID and protected by a password). When Oracle BPEL Process Manager is installed, an initial domain named 'default' is created. If you inspect the location of any deployed process, you will see that the domain makes up part of the URL, for example: http://[hostname]:[port]/orabpel/[domain]/[process name]/[process version] So using this approach we can create a domain called “private”. By doing this we can define a directive to Apache to prohibit access to any URL that contains the pattern “/orabpel/private/”. You can create a new domain from the BPEL console. From the initial login screen, instead of login into a particular domain, select the link Goto BPEL Admin (the default password is oracle). From here select the ‘BPEL Domains’ tab and then ‘Create New BPEL Domain’. Then any process we want to make private we simply deploy to this domain. This obviously makes it very simple to make a process “private” with the added benefit is that it also indicates that the process itself is intended to be private. Additional Considerations Now this approach only works when you call the BPEL process via the SOAP stack. Oracle BPEL PM also provides a Java RMI interface; requests made via this interface don’t go via the Oracle HTTP Server. However access via RMI tends to be more tightly controlled so this should not present a serious problem. It doesn’t prevent other clients on the same box from calling the BPEL Process, however again if the client is hosted on the same box then I would hope you have a reasonable handle on what its doing. Finally it doesn’t prevent you from submitting the BPEL process from the BPEL Console as the invocation is made from the BPEL Server itself; however each domain is password protected so this shouldn’t really be an issue. We also mentioned that this approach doesn’t work for the developer install of Oracle BPEL PM as it doesn’t embed the Oracle HTTP Server. However as a default this is probably a good thing as if the process was secured then it would prevent the BPEL Designer in JDeveloper from actually being able to read the WSDL which is required in order to develop the BPEL processes which calls the “private” processes. Of course you can come up with variations of this approach, for example you could conduct developments against a mid tier install and configure the http directive to only allow certain developers (or at least there boxes) to be able to access processes deployed to a particular domain. Conclusion On its own this isn’t a perfect approach to securing a BPEL process, but then it’s not intended to be. But as a relatively simple approach to creating “private” BPEL processes where it is clear that this is the intention, then I believe this is certainly one way of achieving that. However I would be interested in hearing any comments or suggestions on this approach or any alternatives.

########

0 comments: