Tuesday 21 December 2010

Book Review: Oracle SOA Suite 11g Handbook

I knew before I picked up Lucas Jellema's Oracle SOA Suite 11g Handbook it would be a struggle to review it. If you've ever read one of Lucas's huge range of technical blogs, you know he's a prolific technical writer, and if you've ever had the privilege of meeting him you know this comes from his endless energy; I think there's a possibility Lucas is the illegitimate child of the Energizer Bunny. Long story short, I knew when I was asked to review Lucas's book, there was going to be a distinct challenge on how to review everything Lucas will have written.

When reviewing technical books I usually try to think of how I'm going to review the book before I pick it up. There are definitely different approaches to reviewing technical books. Viably you can read a book from front to back and write a summary of each and every chapter. Yet I knew before receiving Lucas's book reviewing every chapter wouldn't be feasible, he will have written to much for me to cover. Couple this with a topic as large and complex as Service Oriented Architecture (SOA) and Oracle's relating products, a comprehensive review would be tiring and as long as the original book defeating the purpose of me writing a review in the first place, I might as well write my own book.

So instead I really wanted to achieve a couple of things in assessing this lofty tomb. Firstly from a professional point of view, how did Lucas's prolific writing style survive the rigours and length of such a lengthy book? For readers looking for a book recommendation this equates to how readable I think the book is.

Secondly, given the vast array of SOA concepts and technologies that would be need to be covered in the book, and given that a book always has high and low points, could I highlight with a choice of a few chapters how much the book varies in quality?

Let's start with the writing style.

Lucas's technical writing style has two main characteristics: 1) in describing any technical problem or solution he'll deliberately take you though the A-B-Cs to ensure you understand the concepts in a logically progressive manner and, 2) such descriptions are coupled with a narrative, usually a story or business case to illuminate what's explained, and sometimes a sharp wit (which I must admit passes me by half the time – it must be a Dutch thing). His writing style has the benefit you'll learn a lot if you take time to read it, but vis-a-vis, you won't learn a lot if you don't give it time as there's a lot to read.

With the A-B-Cs in mind, the nature of the McGraw-Hill "Handbook" series, appears to be a end to end coverage of topics required to know a subject, though not a definitive guide in any specific technology or concept covered by the book. As a Handbook Lucas's text does cover a lot of ground in my opinion (and believe it or not Lucas published extra appendixes online as he couldn't fit all the content in the book), and given his prolific writing style there is definitely more detail than is average in the "Handbook" series, yet readers should not expect complete coverage of each topic. As example the OSB chapter covers the basics of OSB, but not an in-depth look at every feature. This makes the Handbook series great for beginners, and I can't emphasize that enough, but, not in-depth enough for experts looking for specific detail or solutions, such as that provided in a "Cookbook" series. Obviously a book can't be something to everyone.

In addressing the prolific amount of text in the book potential buyers need to ask themselves will they actually read such a lengthy tomb? Yet I can't imagine anyone who was even remotely interested in SOA technologies looking for a quick fix here, so the detail provided in Lucas's book I think I can safely recommend.

Looking at a chapter: Oracle Service Bus

Of the number of chapters I was particularly interested to read, that of the Oracle Service Bus was high on the list. There's a good coverage here, with:

a) A general discussion on the role of the enterprise service bus. Having read a few texts on service buses including David Chappell's Enterprise Service Bus - Theory in Practice, I was pleased to find nuggets beyond just the expected descriptions. In particular the OSB chapter introduces the VET(R)O pattern that an ESB should implement. This is one of the delights in Lucas's book, you may think you're going to get a slow feature by feature explanation on Oracle's SOA offerings, but here's an example where he brings in outside ideas to further explain concepts, and gives the reader an opportunity with a bit of Googling to discover new areas of computing.

b) An example in context of the overall story told in the book, that of St Matthews Hospital in the Netherlands in their adoption of SOA. The ongoing example is an important crutch to the book in order to assist readers to understand the relevance of the SOA technologies described in context of a real business case.

c) A history of Oracle's OSB technologies. I must admit having done some research for a customer some years back keeping track of Oracle's service bus products, acquisitions and rebranding is a hard task, of which the book tackles well.

d) A look at implementing OSB's proxies and business services in context of the St Matthews story, introducing the main OSB concepts and their implementation.

e) And importantly in context of Oracle's OSB examples of using an email adapter and REST web services. These are particularly smart additions to the overall chapter in that they address a common bias that OSBs are very limited in appeal, essentially only SOAP routing engines. With the email interface it shows OSB's flexibility, and with REST capabilities a modern take on web service design.

Looking at another chapter: Tactical Management and Governance

Another chapter of personal interest was that covering governance. To me (remembering I'm not a SOA expert) governance is the discipline and expertise a senior developer or architect can bring to a project regardless of the technology at hand. This chapter does indeed introduce SOA governance, but then shows why it's a book on Oracle's SOA Suite 11g rather than SOA in general, as it then investigates Oracle's MetaData Services as a technical solution to some of the issues addressed by governance. As I opened earlier in this review, this shows the book possibly at its lowest point, as it covers (what I believe to be) the important topic of governance too briefly, and dwells on the Oracle solution more than the problem at hand. To be fair, when I say lowest point, the overall average is quite high so there's still much to learn from the chapter, but as a SOA beginner I feel left short in this chapter. Also to be fair, while Lucas does a great effort in covering SOA outside of the context of Oracle's SOA Suite, the book is indeed the SOA Suite 11g Handbook, not a theoretical book on SOA, so this chapter fits well.

Other take-aways

I mentioned in the OSB chapter that the book takes time out to discuss the history of OSB. In a similar vein the 3rd chapter of the book is a worthy read in its own right, discussing:

a) A history of standardisation in the web service arena and how this lead to service oriented architecture, including a long list of standards and when they were published, to show you the amount of work in this area and what has been achieved.

b) A history of Oracle Corporation, and how it moved from being a database company to in addition a middleware company. This includes an indepth look of Oracle and its acquisitions. This is probably the best summary I've read on the issue and really puts in context of what Oracle is today and why they make many decisions that they do

c) The emergence of Oracle's "Fusion", including Fusion Applications and Fusion Middleware as a platform.

From here there's a great description of the parts of SOA. I must admit even though I've been working in the FMW area for sometime, before reading this book I didn't know my BAMs from my BPMs. Having read this key chapter I'm now full bottle, well, at least enough so I don't sound like a complete newbie discussing SOA in front of other experts. This is kind of the launching pad that I needed many years ago to jump from PL/SQL to Java in understanding the hundreds of terms, acronyms, products and more – well worth the read in getting your head around the SOA space.

Conclusion

The Oracle SOA Suite 11g Handbook covers a lot of ground, because by necessity SOA is a large topic, SOA Suite has a large array of technical solutions, and Lucas is a prolific writer. This book will be a good choice for any beginner wanting breadth of coverage of all SOA topics, and offers excellent value for money because so much is covered. For experts looking for a more in-depth coverage of each individual topic this book is not for you, but would provide a good reference for specialists who need to revisit the basics after sometime away from the SOA arena.

Phew, now to review something a little shorter, say something under 600 pages!

Tuesday 14 December 2010

ADF: changing your application's URL (or what on earth is a context-root anyway?)

When you start running ADF applications under JDev 11g you'll become familiar with seeing URLs like the following in your browser:

http://127.0.0.1:7101/Sage-ViewController-context-root/faces/Welcome.jspx

Each part of the URL is significant, though the one that stands out as being odd is the rather cumbersome "Sage-ViewController-context-root". This part of the URL in Java EE parlance is called the context-root, and not just because our example literally has context-root written into it.

The context-root at its most basic form is the unique part of the URL on the application server to clearly distinguish your application from another on the server. However as you can see in this example the "Sage-ViewController-context-root" is hardly something you'd want to present to users. Something more palatable like "Sage" or "HR" would be useful, delivering an end URL like:

http://127.0.0.1:7101/Sage/faces/Welcome.jspx

So how do we go about making a friendly context-root for our applications? Essentially there's 2 locations.

Design time

If you're running your application from JDeveloper by opening & running a jspx page via the right mouse menu or similar, when the application is deployed to the integrated WLS server, it will use Java EE Application options under the ViewController's Project Properties, accessible by right clicking the same name option of the ViewController project:

As you can see in the above picture, the Java EE Web Context Root is set to "Sage-ViewController-context-root". Simply change this to something desired.

Deploy time

Once you've finished testing and running your application in your local integrated WLS instance, you're likely to generate an EAR or WAR file to deploy to an external WLS server. In this case the context-root is influenced by settings in the WAR file for the ViewController project. Simply right click the ViewController project -> select Project Properties -> Deployment and locate the associated WAR file deployment profile. In the following picture you can see it's called "Sage_ViewController_webapp1":

If you then select the edit button for the WAR deployment profile, the first node General includes 2 options, namely Use Project's Java EE Web Context Root and Specify Java EE Web Context Root. If you select the 2nd option and enter your own option, this will override the context-root once the application is deployed to an external WLS server:

Wednesday 8 December 2010

Installing OSB 11.1.1.3.0 on Windows

Simple post for my benefit, documenting the minimal install for Oracle Service Bus 11.1.1.3.0 under Windows, configuring the server in development mode. There's nothing overly exciting for other readers in this post, beyond say an abbreviated form of Oracle's verbose install documentation.

Downloads

Oracle WebLogic Server 11gR1 (10.3.3) + Coherence + OEPE – Package Installer – MS Windows (32-bit JVM) ~ 997MB

Oracle Service Bus (11.1.1.3.0) Generic Installer for all platforms
Installing WLS 10.3.3

Run the executable wls1033_oepe111150_win32.exe

Just in case you forgot what you ran, the WLS splash will display in all it's glory:

Once the installer is ready:

Create a new Middleware home:

Choose to live dangerously:

Select a Custom install:

Among the default products and components installed, ensure the Evaluation Database option is selected. This installs Derby used by the development mode OSB server for storing reports.

Install both JDKs:

Use default install directories:

Default node manager options:

Default shortcut location options:

Let the fun begin:

Watching the paint dry:

On completion skip the Run Quickstart:

WLS installation complete.

Installing OSB 10.3.3

cd to Disk1 subdirectory

Run the setup.exe executable. A DOS prompt will display seeking the JRE location on your machine. Point it towards the JRockit JRE directory under WLS:

Another splash screen:

Once the installer is ready to rock n roll:

Select the Custom installer:

Select the default Components to install:

Pass the prerequisite checks:

Change the Oracle Middleware Home to your new WLS home setup in the last section:

Let's kick ass!:

Coffee time!:

Glory days are here again:

Create and configure the WLS Domain

Under the Windows Start Menu, select Oracle WebLogic -> WebLogic Server 11gR1 -> Tools -> Configuration Wizard

Select the Create a new WebLogic Domain option

Select the Oracle Server Bus Extension – Single Server Domain Topology (suitable for dev mode OSB). This will also select the WebLogic Advanced Web Services for JAX-RPC Extension and Oracle JRF options:

Define the domain name:

Set the admin user name and password:

Specify development mode and select the Sun JDK (satisfactory for development mode OSB):

Leave the default option here. This provider will connect to the Derby database as installed in the WLS server, and is used for reporting:

As the WLS server isn't running, the following test of the previous provider will never succeed, the error can be ignored:

Also ignore the resulting dialog, click Ok:

Leave the defaults except the Administration Server option:

Use the default Administration Server options:

Let the fun begin:

Coffee time again!:

On completion leave the Start Admin Server box unselected, and select done.

Starting the WLS server

Via the Windows Start Menu select Oracle WebLogic -> User Projects -> (your domain name) -> Start Server for Oracle Service Bus Domain

This will launch 2 DOS windows, one starting WLS (which will take sometime to start), where you need to wait for the RUNNING message:

....and in the other Window starting Derby:

Entering the OSB Console

Via the Windows Start Menu select Oracle WebLogic -> User Projects -> (your domain name) -> Oracle Service Bus Admin Console

In the resulting browser window login using your WLS admin account and password:

And finally the OSB console will be displayed:

Voila!

Addendum

Coincidentally about 3 minutes after I posted this blog entry, I discovered Peter Paul had completed something similar last week. Doh!

Tuesday 30 November 2010

Using ojdeploy and Ant for creating ADF library JARs

Projects and the Application itself in Oracle's JDeveloper 11g are capable of generating different deployment files at design time, including WAR files, EAR files, JAR files and other standard Java EE archive types. In addition JDev can generate a special JAR type specific to ADF development known as an ADF library. An ADF library differs from standard JARs in that the ADF library includes additional metadata files and constructs required for ADF application development.

To create an ADF library JAR you first need to setup an associated ADF library JAR via the project properties. This is well document in the Fusion Guide.

From here there are essentially 2 ways to actually generate the ADF library JAR. The first is within the IDE via the project right-click deploy options, and applicable to programmers developing in their JDev IDE.

The other option particularly important to build environments is the ojdeploy utility. This tool can be called via the command line or an Ant script, and is necessary for generating ADF library JAR files with the required metadata files and other ADF constructs.

In specifically considering the ant script option for ojdeploy, JDev will create a basic Ant script for building your applications via the New Gallery -> Ant -> Buildfile from Project option. In the dialog that displays selecting the "Include Packaging Tasks (uses ojdeploy)" checkbox ensures the generated Ant script includes an ojdeploy target:

The resulting Ant file will look something like this:
<?xml version="1.0" encoding="UTF-8" ?>
<project name="ViewController" default="all" basedir=".">
<property file="build.properties"/>
<path>....snipped....</path>
<target name="init">
<tstamp/>
<mkdir dir="${output.dir}"/>
</target>
<target name="all" description="Build the project" depends="deploy,compile,copy"/>
<target name="clean" description="Clean the project">
<delete includeemptydirs="true" quiet="true">
<fileset dir="${output.dir}" includes="**/*"/>
</delete>
</target>
<target name="deploy" description="Deploy JDeveloper profiles" depends="init,compile">
<taskdef name="ojdeploy" classname="oracle.jdeveloper.deploy.ant.OJDeployAntTask" uri="oraclelib:OJDeployAntTask"
classpath="${oracle.jdeveloper.ant.library}"/>
<ora:ojdeploy xmlns:ora="oraclelib:OJDeployAntTask" executable="${oracle.jdeveloper.ojdeploy.path}"
ora:buildscript="${oracle.jdeveloper.deploy.dir}/ojdeploy-build.xml"
ora:statuslog="${oracle.jdeveloper.deploy.dir}/ojdeploy-statuslog.xml">
<ora:deploy>
<ora:parameter name="workspace" value="${oracle.jdeveloper.workspace.path}"/>
<ora:parameter name="project" value="${oracle.jdeveloper.project.name}"/>
<ora:parameter name="profile" value="${oracle.jdeveloper.deploy.profile.name}"/>
<ora:parameter name="nocompile" value="true"/>
<ora:parameter name="outputfile" value="${oracle.jdeveloper.deploy.outputfile}"/>
</ora:deploy>
</ora:ojdeploy>
</target>
<target name="compile" description="Compile Java source files" depends="init">
<javac destdir="${output.dir}" classpathref="classpath" debug="${javac.debug}" nowarn="${javac.nowarn}"
deprecation="${javac.deprecation}" encoding="UTF-8" source="1.6" target="1.6">
<src path="src"/>
</javac>
</target>
<target name="copy" description="Copy files to output directory" depends="init">
<patternset id="copy.patterns">
<include name="**/*.gif"/>
<include name="**/*.jpg"/>
<include name="**/*.jpeg"/>
<include name="**/*.png"/>
<include name="**/*.properties"/>
<include name="**/*.xml"/>
<include name="**/*.ejx"/>
<include name="**/*.xcfg"/>
<include name="**/*.cpx"/>
<include name="**/*.dcx"/>
<include name="**/*.sva"/>
<include name="**/*.wsdl"/>
<include name="**/*.ini"/>
<include name="**/*.tld"/>
<include name="**/*.tag"/>
<include name="**/*.xlf"/>
<include name="**/*.xsl"/>
<include name="**/*.xsd"/>
</patternset>
<copy todir="${output.dir}">
<fileset dir="src">
<patternset refid="copy.patterns"/>
</fileset>
</copy>
</target>
</project>
In particular notice the Ant targets: init, clean, deploy, compile and copy. Note within the deploy target a call to the ojdeploy utility.

Now you'd think with these 5 targets generated by JDeveloper that they are all co-related and have dependencies. You can even see the deploy-ojdeploy target has dependencies on the init and compile targets. Unfortunately this is misleading.

The ojdeploy utility written by Oracle is in fact capable of doing all the other targets' tasks. The utility takes care of creating/initializing the destination directories, it cleans and compiles the specified application or project, and it takes care of copying all class files. In fact if you leave the other targets in the Ant script this can interfere with the operation of the ojdeploy utility and you should in fact remove them. Instead you should have something like this:

<project name="ViewController" default="all" basedir=".">
<property file="build.properties"/>
<path>....snipped....
<target name="deploy" description="Deploy JDeveloper profiles" depends="init,compile">
<taskdef name="ojdeploy" classname="oracle.jdeveloper.deploy.ant.OJDeployAntTask" uri="oraclelib:OJDeployAntTask"
classpath="${oracle.jdeveloper.ant.library}"/>
<ora:ojdeploy xmlns:ora="oraclelib:OJDeployAntTask" executable="${oracle.jdeveloper.ojdeploy.path}"
ora:buildscript="${oracle.jdeveloper.deploy.dir}/ojdeploy-build.xml"
ora:statuslog="${oracle.jdeveloper.deploy.dir}/ojdeploy-statuslog.xml">
<ora:deploy>
<ora:parameter name="workspace" value="${oracle.jdeveloper.workspace.path}"/>
<ora:parameter name="project" value="${oracle.jdeveloper.project.name}"/>
<ora:parameter name="profile" value="${oracle.jdeveloper.deploy.profile.name}"/>
<ora:parameter name="outputfile" value="${oracle.jdeveloper.deploy.outputfile}"/>
</ora:deploy>
</ora:ojdeploy>
</target>
</project>
If you've looked very carefully, you might have noticed I removed the nocompile option for ojdeploy. Bizarrely this boolean option takes three forms: true, false, and, the one you need to use to ensure the correct ADF constructs (such as task-flow-registry.xml) are added to the end ADF library JAR, is remove the nocompile option completely.

(This is bug 9000629 – closed by Support, not considered a bug, but confusing and not intuitive by design)

On the Windows platform you need to be careful that any paths you include in the ojdeploy target within the Ant script (either directly or indirectly via a properties file) exactly match the case of the Windows file system, otherwise similar issues can occur.

(Another bug 10028816 – confirmed bug – fixed 11.1.1.4.0 – patch available for 11.1.1.2.0)

Finally there's also a bug in the ojdeploy workspace option that it can't support the Ant script ${user.dir} property. This isn't applicable to the example above but I thought worth documenting.

(Bug 10028879 – confirmed bug – fixed 11.1.1.4.0 – patch available for 11.1.1.2.0)

Entirely separately to generating ADF libraries, if you wish to use the ojdeploy utility to create an EAR via the workspace, you do this by dropping the project option leaving the workspace, profile and outputfile options. If you do this under JDev 11.1.1.2.0 specifically you'll see the error message "Missing <workspace>, <project> or <profile> parameter in <deploy> element", caused by a bug in the ojdeploy utility, of which there is a patch available.

(Bug 9135159 – confirmed bug – fixed 11.1.1.3.0 – patch available for 11.1.1.2.0)

Addendum

All the above documented via JDev 11.1.1.2.0.

Wednesday 24 November 2010

JDeveloper REST Web Services presentation available

For those who attended the AUSOUG'10 Perth conference, my presentation JDeveloper 11g's REST web services is now available for download.

Thanks to all the members who voted for this session, I was well chuffed when it received 2nd best presentation at the conference.

Tuesday 16 November 2010

Hudson Job SVN "Malformed URL" error

A blog post documenting something we configured incorrectly using Hudson, maybe useful to others.

Via Hudson we build a Java EE application using Ant. The associated Hudson job first checks out the code from SVN. During the SVN check out phase of the job we'd see the following reported in the job console output:

Started by user cmuir
Checking out a fresh workspace because there's no workspace at C:\java\hudson\Hudson1372\build
Checking out https://acme.com/svn/VisualSVNRepo/ADFTaskFlows/REA/trunk
A Build
A Build\Build.jpr
A Build\rea.build.properties
A Build\rea.hudson.properties
A Build\build.xml
A Model
... snip ...
A ViewController\src\acme\rea\taskflow\view\beans\pageFlow
A ViewController\src\acme\rea\taChecking out
ERROR: Failed to check out
org.tmatesoft.svn.core.SVNException: svn: Malformed URL ''
at org.tmatesoft.svn.core.internal.wc.SVNErrorManager.error(SVNErrorManager.java:64)
at org.tmatesoft.svn.core.internal.wc.SVNErrorManager.error(SVNErrorManager.java:51)
at org.tmatesoft.svn.core.SVNURL.(SVNURL.java:221)
at org.tmatesoft.svn.core.SVNURL.parseURIEncoded(SVNURL.java:125)
at hudson.scm.SubversionSCM$ModuleLocation.getSVNURL(SubversionSCM.java:2116)
at hudson.scm.SubversionSCM$CheckOutTask.invoke(SubversionSCM.java:742)
at hudson.scm.SubversionSCM$CheckOutTask.invoke(SubversionSCM.java:660)
at hudson.FilePath.act(FilePath.java:753)
at hudson.FilePath.act(FilePath.java:735)
at hudson.scm.SubversionSCM.checkout(SubversionSCM.java:653)
at hudson.scm.SubversionSCM.checkout(SubversionSCM.java:601)
at hudson.model.AbstractProject.checkout(AbstractProject.java:1046)
at hudson.model.AbstractBuild$AbstractRunner.checkout(AbstractBuild.java:479)
at hudson.model.AbstractBuild$AbstractRunner.run(AbstractBuild.java:411)
at hudson.model.Run.run(Run.java:1248)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:46)
at hudson.model.ResourceController.execute(ResourceController.java:88)
at hudson.model.Executor.run(Executor.java:129)
skflow\view\beans\pageFlow\pageFlow
A ViewController\src\acme\rea\taskflow\view\beans\pageFlow\AuditEvent.java
... snip ...
At revision 3642
Sending e-mails to: chris.muir@acme.com
Finished: FAILURE
In particular note the error:

ERROR: Failed to check out
org.tmatesoft.svn.core.SVNException: svn: Malformed URL ''

From a bit of research we discovered the problem in our specific case was an error in the Hudson job configuration (Hudson console -> Login -> (select specific job) -> Configure). Under the Source Code Management section we'd accidentally created two SVN locations, the 2nd one being blank:


Essentially this second blank entry was causing the error above, where the Malformed "empty" URL '' is the empty SVN location. Simply removing this location from the job configuration solves the issue.

Addendum

Hudson 1.372

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.

Wednesday 3 November 2010

ADF UI Shell: Supporting global hotkeys for the activity tabs

We recently had the requirement to provide our users global hotkeys to switch between tabs within the ADF UI Shell (a.k.a. Dynamic Tab Shell) in JDev 11g. Luckily I caught a presentation at Open World this year where Frank Nimphius showed off support for creating global hotkeys in ADF applications. Frank was kind enough to give me his source code allowing me to modify it to suit the ADF UI Shell specifically. The following code shows the general technique for getting global hotkeys workings, as well as specific code to suit the ADF UI Shell implementation. The following code was built and tested under JDev 11.1.1.2.0.

ADF UI Shell Extension – global hotkeys

Readers familiar with the current incarnation of the ADF UI Shell will know it has the ability to spawn 1 to 15 "activity" tabs displaying whatever the programmer chooses. This provides an ideal framework for users to spawn multiple bounded task flows (BTFs) and work on several data sets all within the one browser window.

Our users have taken to the UI Shell and have been asking for extensions ever since. Of particular note they wanted the ability to open a set of activity tabs, and then flip between them using keyboard shortcuts rather than mouse clicks.

Frank's solution to the rescue.

Solution

The solution requires 3 working parts:

a) JavaScript
b) ViewScoped Managed Bean
c) Changes to the ADF UI Shell derived page to support the hotkeys to call "a" and "b"

The following code shows the general technique.

JavaScript
var keyRegistry = new Array();

keyRegistry[0] = "alt 1";
keyRegistry[1] = "alt 2";
keyRegistry[2] = "alt 3";
keyRegistry[3] = "alt 4";
keyRegistry[4] = "alt 5";
keyRegistry[5] = "alt 6";
keyRegistry[6] = "alt 7";
keyRegistry[7] = "alt 8";
keyRegistry[8] = "alt 9";
keyRegistry[9] = "alt 0";

function registerKeyBoardHandler(serverListener, afdocument) {

_serverListener = serverListener;
var document = AdfPage.PAGE.findComponentByAbsoluteId(afdocument);
_document = document;

for (var i = keyRegistry.length - 1; i >= 0; i--) {
var keyStroke = AdfKeyStroke.getKeyStrokeFromMarshalledString(keyRegistry[i]);
AdfRichUIPeer.registerKeyStroke(document, keyStroke, callBack);
}
}

function callBack(keyCode) {
var activeComponentClientId = AdfPage.PAGE.getActiveComponentId();

// Send the marshalled key code to the server listener for the developer
// To handle the function key in a managed bean method
var marshalledKeyCode = keyCode.toMarshalledString();

// {AdfUIComponent} component Component to queue the custom event on
// {String} name of serverListener
// {Object} params a set of parameters to include on the event.
// {Boolean} immediate whether the custom event is "immediate" -
// which will cause it to be delivered during Apply Request
// Values on the server, or not immediate, in which
// case it will be delivered during Invoke Application.

// Note that if one of the keyboard functions is to create ADF
// bound rows, immediate must be set to false. There is no
// option yet for the ClientEvent to be queued for later -
// InvokeApplication - on the server.
AdfCustomEvent.queue(_document,
_serverListener, {keycode:marshalledKeyCode,
activeComponentClientId:activeComponentClientId}, false);

// indicate to the client that the key was handled and that there
// is no need to pass the event to the browser to handle it
return true;
}
You'll note the hotkey mapping only goes upto 10 (1 to 9 plus zero). The UI Shell does support 15 tabs, but I couldn't think of a nice key combination beyond the 10th.... that can be left up to you.

ViewScoped Managed Bean
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;

import oracle.adf.view.rich.render.ClientEvent;

import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;

import waosr.ui.pattern.dynamicShell.TabContext;


public class KeyboardHandler {

public void registerKeyboardMapping(PhaseEvent phaseEvent) {
if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE) {

FacesContext facesContext = FacesContext.getCurrentInstance();
ExtendedRenderKitService extRenderKitService =
Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
List<UIComponent> childComponents = facesContext.getViewRoot().getChildren();
//First child component in an ADF Faces page - and the only child - is af:document
//Thus no need to parse the child components and check for their component family
//type
String id = ((UIComponent)childComponents.get(0)).getClientId(facesContext);

StringBuffer script = new StringBuffer();
script.append("window.registerKeyBoardHandler('keyboardToServerNotify','" + id + "')");
extRenderKitService.addScript(facesContext, script.toString());
}
}

public void handleKeyboardEvent(ClientEvent clientEvent) {

String keyCode = (String)clientEvent.getParameters().get("keycode");

// The alt+<number> keyboard combination opens the relating ADF UI Shell tab if open
if (keyCode.equalsIgnoreCase("alt 1") || keyCode.equalsIgnoreCase("alt 2") || keyCode.equalsIgnoreCase("alt 3") ||
keyCode.equalsIgnoreCase("alt 4") || keyCode.equalsIgnoreCase("alt 5") || keyCode.equalsIgnoreCase("alt 6") ||
keyCode.equalsIgnoreCase("alt 7") || keyCode.equalsIgnoreCase("alt 8") || keyCode.equalsIgnoreCase("alt 9")) {

String keyIndexStr = keyCode.substring(keyCode.length() - 1, keyCode.length());
int keyIndex = Integer.parseInt(keyIndexStr);
TabContext.getCurrentInstance().setSelectedTabIndex(keyIndex - 1);
}
}
}
Of specific importance the handleKeyboardEvent() method makes use of the ADF UI Shell TabContext class to set the current activity-tab according to the hotkey pressed.

ADF UI Shell page

xmlns:h="http://java.sun.com/jsf/html" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">







method="#{viewScope.keyboardHandler.handleKeyboardEvent}"/>
value="#{bindings.pageTemplateBinding}" id="pt1">






Note:

a) The beforePhase property in the view tag
b) The loading of the JavaScript via the resource tag
c) The serverlistener that calls the bean methods

How This Works

1) Before the page is rendered, JavaScript is added to call the registerKeyBoardHandler method in the attached JavaScript library when the page is rendered
2) The JavaScript method registers (but does not invoke) for the defined keys a server side event
3) On the page rendering, if the user clicks one of the keys, the interaction of the JavaScript server side event *and* the serverListener in the page calls the bean handleKeyboardEvent method
4) Finally the method calls the ADF UI Shell TabContext class to switch tabs based on the hotkey number

Caveat

Frank notes some minor cross browser compatibility issues and certain hotkey combinations not working. Rather than highlighting the specific problems in this release, as this solution is reliant on JavaScript the ever unreliable-pain-in-the-butt-that-it-is-across-different-browser-versions, readers are highly recommended to do their own cross-browser testing.

Saturday 30 October 2010

AUSOUG'10 Perth conference - REST web services with JDev


It's that time of the year again when the Perth Australian Oracle User Group (AUSOUG) conference comes around. This conference is guaranteed to be only 0.005% as large as Oracle Open World, way more personable though, with better lunches, involves less walking & definitely is less tiring. Coffee is better too.

This year I'll be selling Oracle wares with the following double slot presentation:

A change is as good as a REST: JDeveloper 11g's REST web services

23rd November - "Studio 2" - 11am-12:45

Abstract: SOAP based web services can seem a hard slog to develop with their contract first design requirements, and over engineered specification that includes everything plus the kitchen sink. REST based web services provide light relief for quick web service development where the constraints of SOAP web services feel extreme for some short sharp development. Luckily Oracle's JDeveloper provides support for both styles, and in this presentation Chris Muir will demonstrate a short and sweet demonstration to getting REST web services up and running, using JDeveloper's JAX-RS support.


I look forward to seeing you there.

Tuesday 26 October 2010

Closing Applications (Correctly) in JDeveloper

The following documents a somewhat small issue we're having with JDeveloper 11.1.1.2.0. At this time I'm unable to lodge an SR with Oracle Support as we don't understand the circumstances under which it occurs, thus we can't build the usual simple "Hello World" test case that Support so thrives on. However as we've identified the symptoms of the problem and the associated workaround I'll publish them here for others to benefit (including my team) so we don't hit the issue again.

Note this issue is only verified under JDev 11.1.1.2.0. I have no knowledge if the issue can be replicated in other JDev versions as this stage.

Issue Symptoms

From time to time after checking out an application from SVN inside JDeveloper:



....we'd discover JDev has incidentally modified one of the project files. In the following example the ViewController project is italicized meaning it has been modified:


Pressing "Save all" and then comparing the modified file against the previous revision reveals that the ViewController project has reverted back to the default settings, losing all the changes from the previous ViewController revision:


With the ViewController modified, the project can no longer compile (because among other things it's lost the attached libraries), and the change is effectively destructive. Whatever you do, don't check the ViewController project file back into SVN as it's just plain wrong.

Workaround #1

If you've arrived at this point where a project has been automatically modified by JDeveloper when checked out of SVN, the workaround in this situation is to Revert the project file back to the previous revision.

A Known Unknown Bug

In the example above I've identified the issue with a ViewController project in an ADF application. However note this problem isn't specific to any project type (e.g. Model, ViewController, whatever). We've experienced this issue across different projects in different applications from time to time. The converse of this issue is there's been a whole lot of projects this issue doesn't occur for too (i.e. in the above example, the Model project wasn't incidentally modified too), so the problem has something to do with the specific project configuration where the error occurs. Unfortunately we're unsure what that specific configuration is. However once the problem is detected it is consistently replicable on the problematic project at hand.

Bug Preconditions

While we haven't been able to work out what configuration in the project files causes the bug, we are aware of a set of steps that do lead up to the bug.

From a day by day basis our programmers will have an application open in JDeveloper, synced with an SVN repository:


From time to time the programmer makes a decision that rather than syncing each individual file change coming from the SVN repository into the application, it's just easier to delete the local working copy of the application and check out a whole brand new copy. To do this the developer:

a) Closes JDeveloper
b) Identifies the working copy application directory on the filesystem
c) Deletes it
d) Opens JDeveloper
e) Checks out the same application from the SVN Repository into the *same* directory that the previous working copy was checked out

It's at this point we see the issue described, but as mentioned, not for all application projects, just some projects, sometimes. However once this problem occurs, repeating steps "a" to "e" from above the issue is consistently reproducible.

From what we can see for step "e" is if we check out to any other directory, the problem isn't replicated.

What we found odd about this issue is if another developer on another machine checks out the same application, the said problematic project file wouldn't be automatically modified by JDeveloper. Yet this gave us the spark of an idea of what's going wrong and how to avoid it in the first place.

Workaround #2

You'll note in the "a" to "e" description above, the developer closes JDev, then deletes the application from the file system, then re-opens JDev. What we're not giving the JDev IDE here is a chance to recognize that one of the applications it had opened has been removed completely from the file system.

The IDE seems to be partially smart in that the application is removed from the application poplist at the top of the Application Navigator when we reopen JDeveloper. Yet if we then proceed to check out the same application as described in "a" to "e" we hit the described problem.

A workaround to the problem is before closing the JDev IDE, via the Application menu selecting Close, to close the selected application. If this option is done first the end problem is not seen. The conclusion is this gives JDev a chance to tidy up it's internal state about which applications and projects are open, and somehow this avoids the bug we're experiencing.

Arguably the IDE should be able to handle this situation regardless. The fact that the bug is destructive to the project configuration is a major concern, especially if a junior programmer doesn't understand what's happening and checks in the changed project file regardless, but in the end following the workaround steps here avoid the issue in the first place.