Sunday, March 29, 2009

Mainpage and Task Flow region interaction

In JDeveloper 11g a Task Flow region can exists isolated in your ADF page, but there are ways to interact with this region. For example you can use input parameters like I did in this example or you can pass ADF pagedef events to the region like I did in this example. In this blog I will use queueActionEventInRegion method to start a navigation action in the Task Flow from the mainpage so it goes to the next page fragment and I will use a regionNavigationListener on the region to get the current displayed page fragment of the region so I can use this in the mainpage to disable some buttons. ( Thanks David Giammona for the great region interaction paper ).
Here some pics of the example I made. In the mainpage I have two buttons with these buttons I pass a event to the region so it goes to the next pagefragment. When this happens the regionNavigationListener is fired and the new displayed view is passed on the mainpage so I can disable the right button.




The bounded Task Flow I use for this example with two pages and some control flow cases between these two fragments
Here is the mainpage jsf page code with the regionNavigationListener on the af:region component.

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<af:document>
<af:form>
<af:panelHeader text="Main page">
<f:facet name="context">
<af:panelGroupLayout layout="horizontal">
<af:outputLabel value="Old view"/>
<af:outputText value="#{Main.oldRegionView}"
id="old"/>
<af:spacer width="50" height="1"/>
<af:outputLabel value="New view"/>
<af:outputText value="#{Main.newRegionView}"
id="new"/>
</af:panelGroupLayout>
</f:facet>
<f:facet name="legend">
<af:toolbar>
<af:commandToolbarButton text="Page1"
id="buttonPage1"
actionListener="#{Main.pageOneActionListener}"
disabled="#{Main.newRegionView eq '/region-task-flow/page1'}"/>
<af:commandToolbarButton text="Page2"
id="buttonPage2"
actionListener="#{Main.pageTwoActionListener}"
disabled="#{Main.newRegionView eq '/region-task-flow/page2'}"/>
</af:toolbar>
</f:facet>
<af:panelBox text="Task Flow Region" showDisclosure="false">
<f:facet name="toolbar"/>
<af:region value="#{bindings.regiontaskflow1.regionModel}"
id="regio1"
binding="#{Main.region}"
regionNavigationListener="#{Main.navigationListener}"/>
</af:panelBox>
</af:panelHeader>
</af:form>
</af:document>
</f:view>
</jsp:root>

and here is backing bean code where I pass a queueActionEventInRegion event so it navigates to the other page fragment.

package nl.whitehorses.tf.region.view.backing;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.PhaseId;

import oracle.adf.view.rich.component.rich.fragment.RichRegion;
import oracle.adf.view.rich.context.AdfFacesContext;
import oracle.adf.view.rich.event.RegionNavigationEvent;

public class MainPage {
private RichRegion region;
private String OldRegionView;
private String NewRegionView;
private String outcome;

public MainPage() {
}

public void pageOneActionListener(ActionEvent actionEvent) {
outcome = "to1";
region.queueActionEventInRegion(getMethodExpression("#{Main.getOutcomeExpression}"),
null, null, false, -1, -1,
PhaseId.ANY_PHASE);
}

public void pageTwoActionListener(ActionEvent actionEvent) {
outcome = "to2";
region.queueActionEventInRegion(getMethodExpression("#{Main.getOutcomeExpression}"),
null, null, false, -1, -1,
PhaseId.ANY_PHASE);
}

private MethodExpression getMethodExpression(String name) {
FacesContext facesCtx = FacesContext.getCurrentInstance();
Application app = facesCtx.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesCtx.getELContext();
return elFactory.createMethodExpression(elContext, name, String.class, new Class[] { });
}


public String getOutcomeExpression() {
return outcome;
}

public void navigationListener(RegionNavigationEvent event) {
NewRegionView = event.getNewViewId();
OldRegionView = event.getOldViewId();
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("old"));
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("new"));
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("buttonPage1"));
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("buttonPage2"));
}

private UIComponent getUIComponent(String name) {
FacesContext facesCtx = FacesContext.getCurrentInstance();
return facesCtx.getViewRoot().findComponent(name);
}

public void setRegion(RichRegion region) {
this.region = region;
}

public RichRegion getRegion() {
return region;
}

public String getOldRegionView() {
return OldRegionView;
}

public String getNewRegionView() {
return NewRegionView;
}

}

Here is the example workspace.

Sunday, March 22, 2009

Calling OSB services from BPEL

In my previous blog I already talked about calling BPEL services from OSB. In this blog entry I will show you how to call an synchronous OSB proxy service from BPEL. If you have problems to get this working, please see my previous blog entry how to solve this. I got this working with OSB 10.3 and Soa Suite 10.1.3.4 MLR5.
First we need to have an OSB proxy service with an WSDL.
As transport protocol we will use the SB protocol
Export the WSDL of the OSB Proxy service and put the wsdl and xsd's in your BPEL project.

When we take a look at the WSDL we can see that the OSB proxy service address is sb://xxx:70001/w..

In the BPEL project we can add a partnerlink, where we will use the OSB proxy wsdl.

Next step is to add an invoke process activity where we can select the OSB Partnerlink and we have to create an input and output variable here.


You only have to add some assign process activities to fill or retrieve the input and output variable.

Tuesday, March 17, 2009

Calling (A)Sync BPEL services from OSB with ormi / opmn

With OSB 10.3 ( BEA Aqualogic Service Bus ) you can call BPEL services with the ormi or opmn protocol ( just like OESB ). With version 10.3, OSB support the bpel 10.1.3.4 transport. In this blog I will show you how to call the BPEL services from OSB. Calling OSB services from BPEL is also possible but I'll explain this in an other blog entry. I got this working with the MLR5 patch for Soa suite 10.1.3.4.
First we have to do some hacking else we can get this error com.evermind.server.rmi.RMIConnectionException: Disconnected: javax.xml.namespace.QName; local class incompatible: stream classdesc serialVersionUID
First we have to add a java parameter to the OSB server. Go to domains\osb_domain\bin folder and edit the setDomainEnv file and add this line to this file. set JAVA_PROPERTIES=-Dplatform.home=%WL_HOME% -Dwls.home=%WLS_HOME% -Dweblogic.home=%WLS_HOME% -Dcom.sun.xml.namespace.QName.useCompatibleSerialVersionUID=1.0
Step 2 is to replace the bpel jars in the bpel transport ear. Go folder osb_10.3\lib\transports and open bpel10gtransport.ear and replace the following jar files orabpel.jar, orabpel-common.jar , xmlparserv2.jar and oc4jclient.jar with the ones you can find in the soa suite 10.1.3 home.
Now we are ready to call a Synchronous BPEL service from OSB. Here is a overview picture how it works for a Synchronous Service
First we create a proxy service in the OSB where we use the WDSL of the BPEL service.

Create message flow where we call the Business service which uses the BPEL transport.

Create the OSB Business service where we use the input operation of the BPEL wsdl. OSB detects that this is a BPEL wsdl

Select the bpel-10g transport. As endpoint we can choose between opmn or ormi. For ormi we can use this url ormi://localhost:12401/default/BPELProcess_Sync Where 12401 is the rmi port of the OC4J container and BPELProcess_Sync is the name of the BPEL service. Don't use orabpel in the url. You can also use opmn://localhost:6003/home/default/BPELProcess_Sync where 6003 is the opmn port and home is the name of the OC4J container.
Because this is the wsdl of a Synchronous BPEL proces we have to choose Synchronous client and select a service account ( this contains the oc4jadmin username / password )

That's all for a Synchronous BPEL service.

For an Asynchronous BPEL we have to do a little more. See this overview picture.
Import the wsdl of the Asynchronous BPEL process and use this in a new Proxy Service.

Do the same for a new Business service, this business service is called from the message flow of the just created proxy service.

Select as protocol bpel-10g and use as endpoint opmn://localhost:6003/home/default/BPELProcess_Async.

Now we have to select Asynchronous Client and as callback proxy we have to select a return proxy service and select a service account ( this contains the oc4jadmin username / password )

Now we create a call back proxy service where we select the callback operation of the BPEL service.

Now we have to select SB as protocol

this proxy service calls a business service with the same BPEL wsdl callback operation.

I store the result of the BPEL service in JMS queue

that's all for the Asynchronous BPEL service. For more information see the OSB bpel transport page.

Saturday, March 14, 2009

Some handy code for backing beans ( ADF & JSF )

Here some code which you can use in your backing beans, I use this code all the time. With this you can retrieve the data or actions from the ADF page definition or create new uicomponents with ADF definitions and some JSF methods.

I'll keep this page up to date. Let me know if you have some code.

// print the roles of the current user
for ( String role : ADFContext.getCurrent().getSecurityContext().getUserRoles() ) {
System.out.println("role "+role);
}


// get the ADF security context and test if the user has the role users
SecurityContext sec = ADFContext.getCurrent().getSecurityContext();
if ( sec.isUserInRole("users") ) {
}
// is the user valid
public boolean isAuthenticated() {
return ADFContext.getCurrent().getSecurityContext().isAuthenticated();
}
// return the user
public String getCurrentUser() {
return ADFContext.getCurrent().getSecurityContext().getUserName();
}


// get the binding container
BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

// get an ADF attributevalue from the ADF page definitions
AttributeBinding attr = (AttributeBinding)bindings.getControlBinding("test");
attr.setInputValue("test");

// get an Action or MethodAction
OperationBinding method = bindings.getOperationBinding("methodAction");
method.execute();
List errors = method.getErrors();

method = bindings.getOperationBinding("methodAction");
Map paramsMap = method.getParamsMap();
paramsMap.put("param","value") ;
method.execute();


// Get the data from an ADF tree or table
DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();

FacesCtrlHierBinding treeData = (FacesCtrlHierBinding)bc.getControlBinding("tree");
Row[] rows = treeData.getAllRowsInRange();

// Get a attribute value of the current row of iterator
DCIteratorBinding iterBind= (DCIteratorBinding)dcBindings.get("testIterator");
String attribute = (String)iterBind.getCurrentRow().getAttribute("field1");

// Get the error
String error = iterBind.getError().getMessage();


// refresh the iterator
bindings.refreshControl();
iterBind.executeQuery();
iterBind.refresh(DCIteratorBinding.RANGESIZE_UNLIMITED);

// Get all the rows of a iterator
Row[] rows = iterBind.getAllRowsInRange();
TestData dataRow = null;
for (Row row : rows) {
dataRow = (TestData)((DCDataRow)row).getDataProvider();
}

// Get the current row of a iterator , a different way
FacesContext ctx = FacesContext.getCurrentInstance();
ExpressionFactory ef = ctx.getApplication().getExpressionFactory();
ValueExpression ve = ef.createValueExpression(ctx.getELContext(), "#{bindings.testIter.currentRow.dataProvider}", TestHead.class);
TestHead test = (TestHead)ve.getValue(ctx.getELContext());

// Get a session bean
FacesContext ctx = FacesContext.getCurrentInstance();
ExpressionFactory ef = ctx.getApplication().getExpressionFactory();
ValueExpression ve = ef.createValueExpression(ctx.getELContext(), "#{testSessionBean}", TestSession.class);
TestSession test = (TestSession)ve.getValue(ctx.getELContext());

// main jsf page
DCBindingContainer dc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
// taskflow binding
DCTaskFlowBinding tf = (DCTaskFlowBinding)dc.findExecutableBinding("dynamicRegion1");
// pagedef of a page fragment
JUFormBinding form = (JUFormBinding) tf.findExecutableBinding("regions_employee_regionPageDef");
// handle to binding container of the region.
DCBindingContainer dcRegion = form;



// return a methodexpression like a control flow case action or ADF pagedef action
private MethodExpression getMethodExpression(String name) {
Class [] argtypes = new Class[1];
argtypes[0] = ActionEvent.class;
FacesContext facesCtx = FacesContext.getCurrentInstance();
Application app = facesCtx.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesCtx.getELContext();
return elFactory.createMethodExpression(elContext,name,null,argtypes);
}

//
RichCommandMenuItem menuPage1 = new RichCommandMenuItem();
menuPage1.setId("page1");
menuPage1.setText("Page 1");
menuPage1.setActionExpression(getMethodExpression("page1"));

RichCommandButton button = new RichCommandButton();
button.setValueExpression("disabled",getValueExpression("#{!bindings."+item+".enabled}"));
button.setId(item);
button.setText(item);
MethodExpression me = getMethodExpression("#{bindings."+item+".execute}");
button.addActionListener(new MethodExpressionActionListener(me));
footer.getChildren().add(button);


// get a value
private ValueExpression getValueExpression(String name) {
FacesContext facesCtx = FacesContext.getCurrentInstance();
Application app = facesCtx.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesCtx.getELContext();
return elFactory.createValueExpression(elContext, name, Object.class);
}
// an example how to use this
RichInputText input = new RichInputText();
input.setValueExpression("value",getValueExpression("#{bindings."+item+".inputValue}"));
input.setValueExpression("label",getValueExpression("#{bindings."+item+".hints.label}"));
input.setId(item);
panelForm.getChildren().add(input);



// catch an exception and show it in the jsf page
catch(Exception e) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}


FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_WARN, msgHead , msgDetail);
facesContext.addMessage(uiComponent.getClientId(facesContext), msg);


// reset all the child uicomponents
private void resetValueInputItems(AdfFacesContext adfFacesContext,
UIComponent component){
List<UIComponent> items = component.getChildren();
for ( UIComponent item : items ) {

resetValueInputItems(adfFacesContext,item);

if ( item instanceof RichInputText ) {
RichInputText input = (RichInputText)item;
if ( !input.isDisabled() ) {
input.resetValue() ;
adfFacesContext.addPartialTarget(input);
};
} else if ( item instanceof RichInputDate ) {
RichInputDate input = (RichInputDate)item;
if ( !input.isDisabled() ) {
input.resetValue() ;
adfFacesContext.addPartialTarget(input);
};
}
}
}

// redirect to a other url
ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext();
HttpServletResponse response = (HttpServletResponse)ectx.getResponse();
String url = ectx.getRequestContextPath()+"/adfAuthentication?logout=true&end_url=/faces/start.jspx";

try {
response.sendRedirect(url);
} catch (Exception ex) {
ex.printStackTrace();
}

// PPR refresh a jsf component
AdfFacesContext.getCurrentInstance().addPartialTarget(UIComponent);


// find a jsf component
private UIComponent getUIComponent(String name) {
FacesContext facesCtx = FacesContext.getCurrentInstance();
return facesCtx.getViewRoot().findComponent(name) ;
}


// get the adf bc application module
private OEServiceImpl getAm(){
FacesContext fc = FacesContext.getCurrentInstance();
Application app = fc.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = fc.getELContext();
ValueExpression valueExp =
elFactory.createValueExpression(elContext, "#{data.OEServiceDataControl.dataProvider}",
Object.class);
return (OEServiceImpl)valueExp.getValue(elContext);
}


// change the locale
Locale newLocale = new Locale(this.language);
FacesContext context = FacesContext.getCurrentInstance();
context.getViewRoot().setLocale(newLocale);


// get the stacktrace of a not handled exception
private ControllerContext cc = ControllerContext.getInstance();

public String getStacktrace() {
if ( cc.getCurrentViewPort().getExceptionData()!=null ) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
cc.getCurrentViewPort().getExceptionData().printStackTrace(pw);
return sw.toString();
}
return null;
}


// get the selected rows from a table component
RowKeySet selection = resultTable.getSelectedRowKeys();
Object[] keys = selection.toArray();
List<Long> receivers = new ArrayList<Long>(keys.length);
for ( Object key : keys ) {
User user = modelFriends.get((Integer)key);
}

// get selected Rows of a table 2
for (Object facesRowKey : table.getSelectedRowKeys()) {
table.setRowKey(facesRowKey);
Object o = table.getRowData();
JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding)o;
Row row = rowData.getRow();
Test testRow = (Test)((DCDataRow)row).getDataProvider() ;
}

Thursday, March 12, 2009

Generate resource bundles for ADF

JDeveloper ADF supports the use of resource bundles for the label and tooltip values of an attribute. This attribute can exists in an ADF datacontrol class or an ADF BC object. In this blog I will show you how you can set these values manually or generate these values with ANT.
First we will do this manually. In every project you can set the resource bundle option. Default is one resource bundle per project.
Now you can open the ADF BC entity or the ADF Datacontrol class xml file and select the attribute and press the edit button. Now you can put in your value for a label or a tooltip
If you already have a resource bundle than you can select a text resource in this resource bundle
This is an example of an ADF datacontrol class file with a label property and a resource bundle definition.
We can also generate resource bundles with Ant. Steve Muench already made in 2004 a example how to do this. I changed the example a little bit so it works in 11g.
First we have to make a xml with the resource values which matches our attributes of an ADF datacontrol class or an ADF BC object .

<?xml version='1.0'?>
<Mod>
<Ent name="Data" package="nl.whitehorses.model">
<Att name="lovLabel">
<Msg label="label" tip="label in nederlands"/>
<Msg lang="en" label="label" tip="label in english"/>
</Att>
<Att name="lovValue">
<Msg label="waarde" tip="Dutch for value"/>
<Msg lang="en" label="value" tip="Engels voor waarde"/>
</Att>
</Ent>
<Ent name="Data2" package="nl.whitehorses.model">
<Att name="lovLabel">
<Msg label="label" tip="label in nederlands"/>
<Msg lang="en" label="label" tip="label in english"/>
</Att>
<Att name="lovValue">
<Msg label="waarde" tip="Dutch for value"/>
<Msg lang="en" label="value" tip="Engels voor waarde"/>
</Att>
</Ent>
</Mod>

Put your xml file with the xslt file and the ant scripts in the root folder of your model project. The xslt file will generate the java resource bundles. Change the build.properties file and set your jdeveloper 11g home. Now we can run the ant scripts. Here you can download the required files.

The generated java resourcebundle looks like this.

package nl.whitehorses.model.common;
// CODE GENERATED BY XSLT From AllMessages.xml - Do Not Edit This File.
import oracle.jbo.common.JboResourceBundle;
public class DataImplMsgBundle_en extends JboResourceBundle {
public DataImplMsgBundle_en() {
}
public Object[][] getContents() {
return super.getMergedArray(sMessageStrings, super.getContents());
}
static final Object[][] sMessageStrings = {
{"lovLabel_LABEL","label"},
{"lovLabel_TOOLTIP","label in english"},
{"lovValue_LABEL","value"},
{"lovValue_TOOLTIP","Engels voor waarde"}
};
}

You can see that label resource has the following name convention attributename_LABEL.
Now we only have to add this resource to the ADF datacontrol class xml file. Open the xml and go the structure window and a ResourceBundle element with a JavaResourceBundle child element.

That's all.

Sunday, March 8, 2009

Very cool JHeadstart 11g TP

Steven and his team released this week a technical preview of JHeadstart 11G. This great add-on for JDeveloper 11G can help you to generate a good looking web application which is based on best practices. This tool is a must have for developers who wants to generate professional ADF 11g web applications. A very good thing about this generator you still can re-use generated Task Flows. If you don't like to generate an application ( really, they exists , I just want to generate everything ,so I can show the customer results and after that I can do cool things ) you can always use this tool for prototyping or for the best practices, this tool is the advanced ADF course, if you understand this you are 11G professional.
Here some pics. this is an one page application with lot of regions.
An edit form

The overview page with an advanced search page

A generated department Task Flow, you can use this Task Flow for your other applications too

JHeadstart still uses velocity templates and now it has page and task flow templates. A lot of templates.

Here is the code of the generated main page, not much JSF code

The application definition editor, Now you have one defintion where you can add services ( in 10.1.3 you had for every ADF BC am an definition file) and we can define global LOV.

I know that I can learn from this tool and I think you too if you are an ADF developer. So money well spend.