Tuesday, 9 November 2010

JDev: Programmatically capturing task flow parameters

We recently had the requirement to log all incoming and outgoing parameters from Bounded Task Flows (BTF) for JDeveloper 11g. Via the kind assistance of Simon Lessard and other OTN Forum helpers (of whom I'm very grateful) we were able to come up with the following solution. I share it here for others to benefit from Simon's advice.

Warning

As per the OTN thread the following solution makes use of "internal" ADF libraries which Oracle gives no guarantee will not change in the future. The following code was tested under 11.1.1.2.0 and is assumed to also work in 11.1.1.3.0, yet you should check carefully that this code works in future versions, as we said, there's no guarantees it will performed as required. Also note Frank Nimphius has raised ER 10198616 to request a public API for the internal ADF libraries used in this solution.

Solution

Developers who are familiar with BTFs in JDev will know that they have an initializer and finalizer property which via EL can call which ever bean code we desire. The following code solution is simply the EL method end points which are called.

I won't bother to explain the code solution, it should be fairly self explanatory:

public class SomeClass {

public void initializer() {
Map taskFlowInputParameters = TaskFlowUtils.getCurrentTaskFlowInputParameters();
logBtfParameters(taskFlowInputParameters);
}

public void finalizer() {
Map taskFlowReturnParameters = TaskFlowUtils.getCurrentTaskFlowReturnParameters();
logBtfParameters(taskFlowReturnParameters);
}

public void logBtfParameters(Map btfParameters) {
HashMap taskFlowParameterValues = new HashMap();

FacesContext facesContext = FacesContext.getCurrentInstance();
Application application = facesContext.getApplication();
AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
Map pageFlowScope = adfFacesContext.getPageFlowScope();

for (Object parameter : btfParameters.values()) {
NamedParameter namedParameter = (NamedParameter)parameter;
String parameterName = namedParameter.getName();
String parameterExpression = namedParameter.getValueExpression();
Object parameterValue;
String stringValue;

if (parameterExpression == null) {
parameterValue = pageFlowScope.get(parameterName);
} else {
parameterValue = application.evaluateExpressionGet(facesContext, parameterExpression, Object.class);
}

if (parameterValue != null) {
try {
stringValue = parameterValue.toString();
} catch (Exception e) {
stringValue = "";
}
} else {
stringValue = "";
}

taskFlowParameterValues.put(parameterName, stringValue);
}
// log the taskFlowParameterValues parameters somewhere
}

}
The code above makes use of the following custom task flow utility class:

import java.util.Map;

import oracle.adf.controller.ControllerContext;
import oracle.adf.controller.TaskFlowContext;
import oracle.adf.controller.TaskFlowId;
import oracle.adf.controller.ViewPortContext;
import oracle.adf.controller.internal.metadata.MetadataService;
import oracle.adf.controller.internal.metadata.NamedParameter;
import oracle.adf.controller.internal.metadata.TaskFlowDefinition;
import oracle.adf.controller.internal.metadata.TaskFlowInputParameter;


/*
* Note this class makes of "internal" classes that Oracle preferred we didn't use (as there's no
* guarantee they wont change. However as of JDev 11.1.1.2.0 there is no other solution for
* retrieving task flow parameter names.
*
* See: http://forums.oracle.com/forums/thread.jspa?threadID=1556568&start=0&tstart=0
*
* Oracle has raised ER 10198616 to create a public API for the internal classes in this case.
*/
public class TaskFlowUtils {

public static TaskFlowId getTaskFlowId() {
ControllerContext controllerContext = ControllerContext.getInstance();
ViewPortContext currentViewPort = controllerContext.getCurrentViewPort();
TaskFlowContext taskFlowContext = currentViewPort.getTaskFlowContext();
TaskFlowId taskFlowId = taskFlowContext.getTaskFlowId();

return taskFlowId;
}

public static TaskFlowDefinition getTaskFlowDefinition(TaskFlowId taskFlowId) {
assert taskFlowId != null;

MetadataService metadataService = MetadataService.getInstance();
TaskFlowDefinition taskFlowDefinition = metadataService.getTaskFlowDefinition(taskFlowId);

return taskFlowDefinition;
}

public static Map getCurrentTaskFlowInputParameters() {
return getInputParameters(getTaskFlowId());
}

public static Map getInputParameters(TaskFlowId taskFlowId) {
assert taskFlowId != null;

TaskFlowDefinition taskFlowDefinition = getTaskFlowDefinition(taskFlowId);
Map taskFlowInputParameters = taskFlowDefinition.getInputParameters();

return taskFlowInputParameters;
}

public static Map getCurrentTaskFlowReturnParameters() {
return getReturnParameters(getTaskFlowId());
}

public static Map getReturnParameters(TaskFlowId taskFlowId) {
assert taskFlowId != null;

TaskFlowDefinition taskFlowDefinition = getTaskFlowDefinition(taskFlowId);
Map namedParameters = taskFlowDefinition.getReturnValues();

return namedParameters;
}
}
Caveat

This code hasn't yet been extensively tested, and in general just shows the programmatic technique for others to understand. Internally we've already generalized this code further to suit our own use case. If you find any bugs or obvious errors with the technique, leave a comment describing the problem you found and any solutions please.

No comments: