Mastering BPM Webinar Series

On Thursdays starting August 18th and running to September 29, Oracle will be hosting a seven part webinar series on Mastering BPM 11g.

For more details see here.  If you would like to attend, please register here.

The September 15 webinar will feature our very own custom BPM worklist sample.

Agenda

Aug 18 – BPM Overview, Manoj Das, Sr. Director Product Management, BPM

Oracle Unified Business Process Management Suite 11g is a complete, unified, and scalable platform that facilitates the management of all types of processes. Learn about Oracle BPM core competencies and market differentiators.

Aug 25 – Mastering BPMN 2.0. Meera Srinivasan, Senior Principal Product Manager, BPM

Oracle BPM is the first solution to support the BPMN Version 2.0 specification standards. Learn about BPMN 2.0 artifacts and how you can leverage concepts such as gateways, reusable sub-processes, and exception handling.

Sep 1 – Ruling with Rules, Heidi Buelow, Senior Principal Product Manager, BPM

The Oracle Business Rules engine can provide agility and flexibility in managing business processes. Learn how BPM addresses rule-based routing and run-time modification of business rules by a business analyst.

Sep 8 – Empowering the Business User, Manoj Das, Sr. Director Product Management, BPM

Learn how Oracle Unified Business Process Management Suite 11g enables business users to be proactive in modeling, managing, and optimizing processes.

Sep 8 – Process Analytics-Intelligent Decision Making, Payal Srivastava, Senior Principal Product Manager, BPM

Hear about options to monitor and manage the performance of your BPM processes. Discover how to leverage the power of process analytics to adapt processes to trends and to optimize operational efficiency.

Sep 15 – BPM APIs, David Read, Senior Principal Product Manager, BPM and Mark Nelson, Consulting Solution Architect, The A-Team

Learn how to extend your BPM implementation using BPM APIs. See how a custom work list can be created using the APIs to suit project needs and to provide flexibility for customers.

Sep 22 – BPM Deployment and High Availability, David Read, Senior Principal Product Manager

Learn about best practices to consider in configuring and deploying Oracle BPM applications. This Webcast will also discuss high-availability concepts and architecture.

Posted in Uncategorized | Tagged , | Leave a comment

Extending Continuous Integration to include Human Task UI projects

If you have been following our series of posts on Continuous Integration, you will have seen that we have got SCA composites containing various components including BPEL, BPMN, Spring and Rules and also SCA Test working in a Hudson/Maven/Subversion continuous integration environment.  In this post, we will extend this to include ADF Human Task UI projects.

Before we dive in, let’s take a look at the overall structure so we can see how this all fits together.  There are a lot of arrows on this diagram, but don’t worry, its not as complicated as it looks.

When we build a BPM or SOA application in JDeveloper, we put it in a ‘Project’ which is kept inside an ‘Application.’  The SOA/BPM project contains the composite, all of our WSDLs, XSDs, adapter configurations, and so on.  The application contains some metadata that is used during build and deployment, importantly for our purposes, it contains the adf-config.xml file which is used to identify which MDS we want to use (if any), and it contains the EAR deployment profile for the human task UI projects.

In addition to this, for every human task we create an ADF UI for, we will have another project to hold that UI.  In the example above, we have two such ADF Human Task UI projects in the application.

In this case, the EAR deployment profile in the application will include the two Human Task UI projects.  Here is an example of what this looks like.  In this example oj1 and oj2 are the Human Task UI projects.

So, in order to automate this build, we need to take two separate sets of actions:

  • We need to compile, package, deploy (and maybe test and paramterize) the composite (SAR)
  • We need to compile and package the Human Task UI projects into WARs and then package those WARs into an EAR and deploy that EAR

To orchestrate this, we will use the Maven Reactor function – which allows us to build multiple projects as part of the build.  The way this works is actually pretty simple.  First we have a top level Maven POM, which we will put in the application, as opposed to one of the projects.  You can see it on the diagram above.  In this POM, we point to the two ‘modules’ that make up the build, i.e. the two POMs in the composite project and the first Human Task UI project.  We only need one POM for all of the Human Task UI projects because we are packaging them all up into a single EAR for deployment.

The POM in the composite project will then be much like the ones we have seen earlier in this series of posts.  It will point to the ant-sca-compile, ant-sca-package, ant-sca-deploy, and ant-sca-test ANT tasks, and to the adf-config.xml for the MDS configuration.  I wont go over this, as it is covered in the previous posts.

The POM in the Human Task UI project is new and different, so we will look at it more closely.  It uses the ojdeploy utility that is included with JDeveloper to compile and package the Human Task UIs into WARs and then an EAR.  It then uses the WebLogic Maven Plugin (also covered previously) to deploy the EAR to WebLogic Server.

So, now that we have the overall picture of how this hangs together, let’s look into the details.

One important thing to note though!  The use of ojdeploy in the build means that the build server must have JDeveloper installed on it.

Here is an example of what the top level POM looks like:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>OJ</groupId>
  <artifactId>TOP</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <scm>
    <connection>scm:svn:https://administrator@bpm.mark.oracle.com/svn/OJ/trunk</connection>
    <developerConnection>scm:svn:https://administrator@bpm.mark.oracle.com/svn/OJ/trunk</developerConnection>
  </scm>

  <modules>
    <!-- FIRST WE HANDLE THE COMPOSITE -->
    <module>OJ</module>
    <!-- THEN THE TASK UI PROJECTS -->
    <module>oj1</module>

  </modules>

  <build>
    <plugins>
      <!-- AGGREGATE JUNIT TEST RESULTS -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-report-plugin</artifactId>
        <version>2.4.2</version>
        <configuration>
          <aggregate>true</aggregate>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <distributionManagement>
    <!-- use the following if you're not using a snapshot version. -->
    <repository>
      <id>local</id>
      <name>local repository</name>
      <url>file:///c:/users/administrator/.m2/repository</url>
    </repository>
    <!-- use the following if you ARE using a snapshot version. -->
    <snapshotRepository>
      <id>localSnapshot</id>
      <name>local snapshot repository</name>
      <url>file:///c:/users/administrator/.m2/repository</url>
    </snapshotRepository>
  </distributionManagement>

</project>

There are two interesting things to note here.  First the modules section, which identifies the two project-level POMs .  The names of the modulse must be the same as the names of directories at the same level as this POM which contains the POM for the project.  Conveniently, JDeveloper keeps the projects in a subdirectory of the application directory, so this is nice and easy for us to do.  When you create the application (top) level POM, make sure you put it in the application directory, not a project directory.

In our example, it is in c:\JDeveloper\mywork\OJ and the project level POMs are in c:\JDeveloper\mywork\OJ\OJ and c:\JDeveloper\mywork\OJ\oj1 for the composite and Human Task UIs respectively.

The second thing to note is the configuration for maven-surefire-report-plugin.  This allows us to aggregate the test results (if any) from all of the modules.  This means that the SCA Test results will be available in the Hudson console.  It means we dont need to use the ‘freestyle’ project like we did in the SCA Test post, we can use the Maven2 project type.

Here is the POM for the composite project:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>OJ</groupId>
  <artifactId>OJ</artifactId>
  <version>1.0-SNAPSHOT</version>

  <parent>
    <groupId>OJ</groupId>
    <artifactId>TOP</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <dependencies>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.6</version>
        <executions>
          <execution>
            <id>sca-compile</id>
            <phase>compile</phase>
            <configuration>
              <target>
                <property name="scac.input" value="${basedir}/composite.xml" />
                <property name="scac.application.home" value="${basedir}/.." />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-compile.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="scac" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
          <execution>
            <id>sca-package</id>
            <phase>package</phase>
            <configuration>
              <target>
                <property name="build.compiler" value="extJavac"/>
                <property name="compositeName" value="${project.artifactId}" />
                <property name="compositeDir" value="${basedir}" />
                <property name="revision" value="${project.version}" />
                <property name="scac.application.home" value="${basedir}/.." />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-package.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="package" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
          <execution>
            <id>sca-deploy</id>
            <phase>deploy</phase>
            <configuration>
              <target>
                <property name="serverURL" value="http://bpm.mark.oracle.com:7001" />
                <property name="user" value="weblogic" />
                <property name="password" value="welcome1" />
                <property name="sarLocation" value="${basedir}/deploy/sca_${project.artifactId}_rev${project.version}.jar" />
                <property name="overwrite" value="true" />
                <property name="forceDefault" value="true" />
                <property name="partition" value="default" />
                <!-- <property name="configplan" value="${basedir}/OJ_cfgplan.xml" /> -->
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-deploy.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="deploy" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
           <execution>
            <id>sca-test</id>
            <phase>deploy</phase>
            <configuration>
              <target>
                <property name="jndi.properties.input" value="c:/Oracle/Middleware/sca-test.jndi.properties" />
                <property name="scatest.input" value="OJ" />
                <property name="scatest.format" value="junit" />
                <property name="scatest.result" value="reports" />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-test.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="test" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

This is the same as the POMs we have already seen, with one exception: I have added a parent section that points back to the top level POM.  I also commented out the SOA configuration plan line in there, of course you can add one if you have one.

Now, here is the POM for the Human Task UI projects:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>OJ</groupId>
  <artifactId>oj1</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>ear</packaging>

  <parent>
    <groupId>OJ</groupId>
    <artifactId>TOP</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <build>
    <plugins>
      <!-- USE OJDEPLOY TO MAKE THE EAR -->
     <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <configuration>
          <executable>c:/oracle/middleware/jdeveloper/jdev/bin/ojdeploy</executable>
          <arguments>
            <argument>-workspace</argument>
            <argument>${basedir}/${project.artifactId}.jws</argument>
            <argument>-profile</argument>
            <argument>${basedir}/deploy/${project.artifactId}.ear</argument>
            <argument>-clean</argument>
          </arguments>
        </configuration>
        <executions>
          <execution>
            <id>ojdeploy</id>
            <phase>deploy</phase>
            <goals>
              <goal>exec</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <!-- WEBLOGIC MAVEN PLUGIN CONFIG - TO DEPLOY THE EAR -->
      <plugin>
        <groupId>com.oracle.weblogic</groupId>
        <artifactId>weblogic-maven-plugin</artifactId>
        <version>10.3.4</version>
        <configuration>
          <adminurl>t3://bpm.mark.oracle.com:7001</adminurl>
          <user>weblogic</user>
          <password>welcome1</password>
          <name>${project.artifactId}</name>
          <remote>true</remote>
          <upload>true</upload>
          <targets>AdminServer</targets>
        </configuration>
        <executions>
          <execution>
            <id>deploy</id>
            <phase>deploy</phase>
            <goals>
              <goal>deploy</goal>
            </goals>
            <configuration>
              <source>deploy/${project.artifactId}.ear</source>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Again, this has a parent section that points back to the top level POM.  Then we use the exec-maven-plugin to execute the ojdeploy utility.  You can see the configuration that passes in the necessary information.  The command that gets executed will look like this:

c:\oracle\middleware\jdeveloper\jdev\bin\ojdeploy
  -workspace c:\jdeveloper\mywork\oj\oj.jws
  -profile oj1
  -clean

In this command, the workspace parameter points to the JDeveloper Workspace (JWS) file for the application (not project).  The profile parameter is the name of the deployment profile in the application (not porject) that is used to build the EAR – we looked at this earlier.  The clean option tells it to delete any previous output and rebuild the whole thing.

Special thanks to Ali Mukadam who tested this and helped me find and correct a problem with my POM which meant that ojdeploy was not being executed.

Running this will cause all of the Human Task UI projects that are included in the deployment profile’s assembly (which should be all of them) into WARs, it will then package all of those WARs into an EAR.  It actually injects some ADF metadata that is needed at runtime in there, which is why we need to use ojdeploy rather than just building the WARs and EAR ourselves using normal Maven functionality – believe me, I tried 🙂

Once that is done, we use the WebLogic Maven Plugin to deploy the EAR to WebLogic.  Again, we have seen the use of this plugin before (here for example), so I wont go over it.

Now that we have all this in place (and your adf-config.xml set up for MDS – see here) you need to check the application (not the project(s), the application) into Subversion.  You do this using the Version… option in the Application menu.  Then you can set up your job in Hudson (as we have done before) using the Maven2 project type and point it to your Subversion root and the top level POM.

Here is example output from a successful build for reference:


Started by user anonymous
Updating https://bpm.mark.oracle.com/svn/OJ/trunk revision: Aug 10, 2011 11:31:32 AM depth:infinity ignoreExternals: false
At revision 5
no change for https://bpm.mark.oracle.com/svn/OJ/trunk since the previous build
Found mavenVersion 2.2.1 from file jar:file:/c:/apache-maven-2.2.1/lib/maven-2.2.1-uber.jar!/META-INF/maven/org.apache.maven/maven-core/pom.properties
Parsing POMs
[workspace] $ c:\java\jdk1.6.0_25/bin/java -classpath c:\Oracle\Middleware\oracle_common\modules\oracle.mds_11.1.1\oramds.jar -cp C:\hudson\plugins\maven-plugin\WEB-INF\lib\maven-agent-2.0.1.jar;c:\apache-maven-2.2.1\boot\classworlds-1.1.jar hudson.maven.agent.Main c:\apache-maven-2.2.1 C:\hudson\war\WEB-INF\lib\hudson-remoting-2.0.1.jar C:\hudson\plugins\maven-plugin\WEB-INF\lib\maven-interceptor-2.0.1.jar 51707 C:\hudson\plugins\maven-plugin\WEB-INF\lib\maven2.1-interceptor-1.2.jar
<===[HUDSON REMOTING CAPACITY]===>���channel started
Executing Maven:  -B -f C:\hudson\jobs\OJ\workspace\pom.xml clean deploy
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO]   Unnamed - OJ:TOP:pom:1.0-SNAPSHOT
[INFO]   Unnamed - OJ:OJ:jar:1.0-SNAPSHOT
[INFO]   Unnamed - OJ:oj1:ear:1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - OJ:TOP:pom:1.0-SNAPSHOT
[INFO]    task-segment: [clean, deploy]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\hudson\jobs\OJ\workspace\pom.xml to C:\.m2\repository\OJ\TOP\1.0-SNAPSHOT\TOP-1.0-SNAPSHOT.pom
[INFO] [deploy:deploy {execution: default-deploy}]
[INFO] Retrieving previous build number from localSnapshot
Uploading: file:///c:/users/administrator/.m2/repository/OJ/TOP/1.0-SNAPSHOT/TOP-1.0-20110810.013138-4.pom
1K uploaded  (TOP-1.0-20110810.013138-4.pom)
[INFO] Retrieving previous metadata from localSnapshot
[INFO] Uploading repository metadata for: 'snapshot OJ:TOP:1.0-SNAPSHOT'
[INFO] Retrieving previous metadata from localSnapshot
[INFO] Uploading repository metadata for: 'artifact OJ:TOP'
[HUDSON] Archiving C:\hudson\jobs\OJ\workspace\pom.xml to C:\hudson\jobs\OJ\modules\OJ$TOP\builds\2011-08-10_11-31-33\archive\OJ\TOP\1.0-SNAPSHOT\pom.xml
[HUDSON] Archiving C:\.m2\repository\OJ\TOP\1.0-SNAPSHOT\TOP-1.0-SNAPSHOT.pom to C:\hudson\jobs\OJ\modules\OJ$TOP\builds\2011-08-10_11-31-33\archive\OJ\TOP\1.0-20110810.013138-4\TOP-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - OJ:OJ:jar:1.0-SNAPSHOT
[INFO]    task-segment: [clean, deploy]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting directory C:\hudson\jobs\OJ\workspace\OJ\target
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\hudson\jobs\OJ\workspace\OJ\src\main\resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] No sources to compile
[INFO] [antrun:run {execution: sca-compile}]
[INFO] Executing tasks

main:

scac:
Validating composite "C:\hudson\jobs\OJ\workspace\OJ/composite.xml"
     [scac] warning: in oj1.task: Task title not specified
     [scac] warning: in oj1.task: Error assignee not specified
     [scac] warning: in oj1.task: Payload not specified
     [scac] warning: in oj2.task: Task title not specified
     [scac] warning: in oj2.task: Error assignee not specified
     [scac] warning: in oj2.task: Payload not specified
[INFO] Executed tasks
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\hudson\jobs\OJ\workspace\OJ\src\test\resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[HUDSON] Recording test results
[INFO] [jar:jar {execution: default-jar}]
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: C:\hudson\jobs\OJ\workspace\OJ\target\OJ-1.0-SNAPSHOT.jar
[INFO] [antrun:run {execution: sca-package}]
[INFO] Executing tasks

main:
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..
    [input] skipping input as property compositeDir has already been set.
    [input] skipping input as property compositeName has already been set.
    [input] skipping input as property revision has already been set.

clean:
     [echo] deleting C:\hudson\jobs\OJ\workspace\OJ/deploy/sca_OJ_rev1.0-SNAPSHOT.jar
   [delete] Deleting: C:\hudson\jobs\OJ\workspace\OJ\deploy\sca_OJ_rev1.0-SNAPSHOT.jar

init:

scac-validate:
     [echo] Running scac-validate in C:\hudson\jobs\OJ\workspace\OJ/composite.xml
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..
    [input] skipping input as property compositeDir has already been set.
    [input] skipping input as property compositeName has already been set.
    [input] skipping input as property revision has already been set.

scac:
Validating composite "C:\hudson\jobs\OJ\workspace\OJ/composite.xml"
     [scac] warning: in oj1.task: Task title not specified
     [scac] warning: in oj1.task: Error assignee not specified
     [scac] warning: in oj1.task: Payload not specified
     [scac] warning: in oj2.task: Task title not specified
     [scac] warning: in oj2.task: Error assignee not specified
     [scac] warning: in oj2.task: Payload not specified

package:
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..
    [input] skipping input as property compositeDir has already been set.
    [input] skipping input as property compositeName has already been set.
    [input] skipping input as property revision has already been set.

compile-source:
    [mkdir] Created dir: C:\hudson\jobs\OJ\workspace\OJ\dist
     [copy] Copying 26 files to C:\hudson\jobs\OJ\workspace\OJ\dist
     [copy] Warning: C:\hudson\jobs\OJ\workspace\OJ\src does not exist.
     [copy] Copying 3 files to C:\hudson\jobs\OJ\workspace\OJ\dist\SCA-INF\classes
      [jar] Building jar: C:\hudson\jobs\OJ\workspace\OJ\deploy\sca_OJ_rev1.0-SNAPSHOT.jar
   [delete] Deleting directory C:\hudson\jobs\OJ\workspace\OJ\dist
[INFO] Executed tasks
[INFO] [oracle-sca-advisor:analyze {execution: sca-advisor}]
[INFO] ------------------------------------------------------------------------
[INFO] ORACLE SCA ADVISOR - ANALYZE
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] Beginning analysis of composite C:\hudson\jobs\OJ\workspace\OJ/composite.xml
[INFO] Scanning...

[INFO] Checking at least one service is defined...
[INFO] Checking at least one component is defined...
[INFO] Checking at least one wire is defined...
[INFO] Checking whether references are using concrete WSDLs...
[INFO] Oracle SCA Advisor Finished
[INFO]
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\hudson\jobs\OJ\workspace\OJ\target\OJ-1.0-SNAPSHOT.jar to C:\.m2\repository\OJ\OJ\1.0-SNAPSHOT\OJ-1.0-SNAPSHOT.jar
[INFO] [deploy:deploy {execution: default-deploy}]
[INFO] Retrieving previous build number from localSnapshot
Uploading: file:///c:/users/administrator/.m2/repository/OJ/OJ/1.0-SNAPSHOT/OJ-1.0-20110810.013138-3.jar
1K uploaded  (OJ-1.0-20110810.013138-3.jar)
[INFO] Retrieving previous metadata from localSnapshot
[INFO] Uploading repository metadata for: 'snapshot OJ:OJ:1.0-SNAPSHOT'
[INFO] Retrieving previous metadata from localSnapshot
[INFO] Uploading repository metadata for: 'artifact OJ:OJ'
[INFO] Uploading project information for OJ 1.0-20110810.013138-3
[INFO] [antrun:run {execution: sca-deploy}]
[INFO] Executing tasks

main:
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..

deploy:
    [input] skipping input as property serverURL has already been set.
    [input] skipping input as property sarLocation has already been set.
setting user/password..., user=weblogic
Processing sar=C:\hudson\jobs\OJ\workspace\OJ/deploy/sca_OJ_rev1.0-SNAPSHOT.jar
Adding sar file - C:\hudson\jobs\OJ\workspace\OJ\deploy\sca_OJ_rev1.0-SNAPSHOT.jar
INFO: Creating HTTP connection to host:bpm.mark.oracle.com, port:7001
INFO: Received HTTP response from the server, response code=200
---->Deploying composite success.
[INFO] Executed tasks
[INFO] [antrun:run {execution: sca-test}]
[INFO] Executing tasks

main:
     [echo] Running scatest using oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..

test:
     [echo] Classpth = c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-ext.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\oracle-soa-client-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-infra-mgmt.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel-common.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel.jar;c:\Oracle\Middleware\wlserver_10.3\server\lib\weblogic.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-api.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-common.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-internal.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jrf_11.1.1\jrf-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-client-stubs-was.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.ejb.thinclient_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.orb_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\plugins\com.ibm.ws.runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.admin.client_7.0.0.jar
     [echo] Running scatest using oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/.. OJ
     [echo] Using context = build.properties
     [echo] Using path = c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-ext.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\oracle-soa-client-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-infra-mgmt.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel-common.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel.jar;c:\Oracle\Middleware\wlserver_10.3\server\lib\weblogic.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-api.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-common.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-internal.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jrf_11.1.1\jrf-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-client-stubs-was.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.ejb.thinclient_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.orb_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\plugins\com.ibm.ws.runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.admin.client_7.0.0.jar
    [input] skipping input as property scatest.input has already been set.
    [input] skipping input as property jndi.properties.input has already been set.
  [scatest] Junit formatting
  [scatest] <testsuite name="sca.default-OJ.codeCoverages" errors="0" failures="0" tests="0" time="0.0"/>
  [scatest] C:\hudson\jobs\OJ\workspace\reports\BPEL-sca.default-OJ.codeCoverages.xml
[INFO] Executed tasks
[HUDSON] Archiving C:\hudson\jobs\OJ\workspace\OJ\pom.xml to C:\hudson\jobs\OJ\modules\OJ$OJ\builds\2011-08-10_11-31-33\archive\OJ\OJ\1.0-SNAPSHOT\pom.xml
[HUDSON] Archiving C:\hudson\jobs\OJ\workspace\OJ\target\OJ-1.0-SNAPSHOT.jar to C:\hudson\jobs\OJ\modules\OJ$OJ\builds\2011-08-10_11-31-33\archive\OJ\OJ\1.0-20110810.013138-3\OJ-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - OJ:oj1:ear:1.0-SNAPSHOT
[INFO]    task-segment: [clean, deploy]
[INFO] ------------------------------------------------------------------------
[INFO] artifact org.codehaus.mojo:exec-maven-plugin: checking for updates from central
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.2/exec-maven-plugin-1.2.pom

Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/mojo-parent/24/mojo-parent-24.pom

Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.2/exec-maven-plugin-1.2.jar

[INFO] [clean:clean {execution: default-clean}]
[INFO] [ear:generate-application-xml {execution: default-generate-application-xml}]
[INFO] Generating application.xml
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\hudson\jobs\OJ\workspace\oj1\src\main\resources
[INFO] [ear:ear {execution: default-ear}]
[INFO] Could not find manifest file: C:\hudson\jobs\OJ\workspace\oj1\src\main\application\META-INF\MANIFEST.MF - Generating one
[INFO] Building jar: C:\hudson\jobs\OJ\workspace\oj1\target\oj1-1.0-SNAPSHOT.ear
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\hudson\jobs\OJ\workspace\oj1\target\oj1-1.0-SNAPSHOT.ear to C:\.m2\repository\OJ\oj1\1.0-SNAPSHOT\oj1-1.0-SNAPSHOT.ear
[INFO] [deploy:deploy {execution: default-deploy}]
[INFO] Retrieving previous build number from localSnapshot
[INFO] repository metadata for: 'snapshot OJ:oj1:1.0-SNAPSHOT' could not be found on repository: localSnapshot, so will be created
Uploading: file:///c:/users/administrator/.m2/repository/OJ/oj1/1.0-SNAPSHOT/oj1-1.0-20110810.013138-1.ear
2K uploaded  (oj1-1.0-20110810.013138-1.ear)
[INFO] Retrieving previous metadata from localSnapshot
[INFO] repository metadata for: 'artifact OJ:oj1' could not be found on repository: localSnapshot, so will be created
[INFO] Uploading repository metadata for: 'artifact OJ:oj1'
[INFO] Uploading project information for oj1 1.0-20110810.013138-1
[INFO] Retrieving previous metadata from localSnapshot
[INFO] repository metadata for: 'snapshot OJ:oj1:1.0-SNAPSHOT' could not be found on repository: localSnapshot, so will be created
[INFO] Uploading repository metadata for: 'snapshot OJ:oj1:1.0-SNAPSHOT'
[INFO] [null:deploy {execution: deploy}]
weblogic.Deployer invoked with options:  -noexit -adminurl t3://bpm.mark.oracle.com:7001 -user weblogic -deploy -name oj1 -source deploy/oj1.ear -targets AdminServer -upload -remote
<Aug 10, 2011 11:32:17 AM EST> <Info> <J2EE Deployment SPI> <BEA-260121> <Initiating deploy operation for application, oj1 [archive: deploy\oj1.ear], to AdminServer .>
Task 0 initiated: [Deployer:149026]deploy application oj1 on AdminServer.
Task 0 completed: [Deployer:149026]deploy application oj1 on AdminServer.
Target state: deploy completed on Server AdminServer

[HUDSON] Archiving C:\hudson\jobs\OJ\workspace\oj1\pom.xml to C:\hudson\jobs\OJ\modules\OJ$oj1\builds\2011-08-10_11-31-33\archive\OJ\oj1\1.0-SNAPSHOT\pom.xml
[HUDSON] Archiving C:\hudson\jobs\OJ\workspace\oj1\target\oj1-1.0-SNAPSHOT.ear to C:\hudson\jobs\OJ\modules\OJ$oj1\builds\2011-08-10_11-31-33\archive\OJ\oj1\1.0-20110810.013138-1\oj1-1.0-SNAPSHOT.ear
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] Unnamed - OJ:TOP:pom:1.0-SNAPSHOT ..................... SUCCESS [2.494s]
[INFO] Unnamed - OJ:OJ:jar:1.0-SNAPSHOT ...................... SUCCESS [28.258s]
[INFO] Unnamed - OJ:oj1:ear:1.0-SNAPSHOT ..................... SUCCESS [40.726s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 minute 12 seconds
[INFO] Finished at: Wed Aug 10 11:32:47 EST 2011
[INFO] Final Memory: 42M/633M
[INFO] ------------------------------------------------------------------------
channel stopped
Finished: SUCCESS

After running the build, everything is deployed in the right place, just as if youdid a deployment from JDeveloper.  Enjoy!

Posted in Uncategorized | Tagged , , , , , | 3 Comments

Adding attachment support to the worklist

I have just posted a new version of the worklist sample which adds the ability to add an attachment to a task.  Let’s take a look how we add and view (download) task attachments.

First, let’s review the addAttachment method in the MTaskList class.  Here is the code (as always, there are more comments in Subversion):

  public static void addAttachment(String user, String taskNumber, MultipartFile attachment) {
    MLog.log("MTaskList", "Entering addAttachment()");

    try {

      // login
      ctx = ContextCache.getContextCache().get(user);

      // get task details
      Task task = getTaskQueryService().getTaskDetailsByNumber(ctx, Integer.parseInt(taskNumber));

      // add the attachment
      getTaskService();
      MLog.log("MTaskList", "Adding attachment to task " + task.getSystemAttributes().getTaskNumber());

      AttachmentType xAttachment = new ObjectFactory().createAttachment();
      xAttachment.setName(attachment.getOriginalFilename());
      xAttachment.setInputStream(attachment.getInputStream());
      xAttachment.setMimeType(attachment.getContentType());
      xAttachment.setDescription(attachment.getOriginalFilename());

      getTaskService().addAttachment(ctx, task.getSystemAttributes().getTaskId(), xAttachment);

      MLog.log("MTaskList", "Leaving addAttachment()");

    } catch (Exception e) {
      e.printStackTrace();
    }

  }

In this code, you can see that we need to get an instance of AttachmentType from the ObjectFactory by calling its createAttachment() method.  We can then fill in the appropriate details – name, MIME type, description, and set the actual content of the attachment.  We need to have our data in an InputStream inside a MultipartFile.  The Spring framework and our controller will handle this for us, let’s take a look at it now.

Here is the code for the AddAttachmentController:

  public AddAttachmentController() {
    setCommandClass(FileUpload.class);
    setCommandName("addattachment.do");
  }

  @Override
  protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response,
      Object command, BindException errors) throws Exception {
    MLog.log("AddAttachmentController", "Entering onSubmit()");
    // cast the uploaded object to our domain class
    FileUpload file = (FileUpload) command;

    // get the file out of the domain object
    MultipartFile multipartFile = file.getFile();

    // check if there is any data in the file
    if (multipartFile != null) {
      MLog.log("AddAttachmentController", "We seem to have a file");
    } else {
      MLog.log("AddAttachmentController", "We do not seem to have a file");
    }

    // handle the attachment here
    String taskNumber = request.getParameter("x_tasknumber");
    MTaskList.addAttachment(request.getRemoteUser(), taskNumber, multipartFile);

    // send the user back to the task detail page (where they were)
    return (new TaskDetailController()).handleRequest(request, response);
  }

We are using the Spring Multipart File Upload capability to get our file from the user into our application.  To do this, we need to define special class which will hold our uploaded file.  This is the FileUpload class shown below.  Notice how the controller calls two special methods to identify this class and the command name (“addattachment.do”).

Next, since we are using a Spring Form here, instead of a POST, we need to implement and override the onSubmit() method.  This is different to what we have seen in our other controllers.  Note that the binary data is passed in to this method by the Spring Form framework as Object command. The first thing we need to do is to cast this to our FileUpload class.  We can then call the getFile() method on this to get the actual MultipartFile.  This is what we pass to our addAttachment method which we saw earlier.  It contains the binary data and also metadata about the file.

Finally, we use the Controller-chaining method to pass the user back to the Task Detail page (where they just came from) which will reload and will now show the attachment we just uploaded.  This is done by returning the output of the handleRequest method on a new instance of the controller for that page, to which we have passed our request and response objects.

public class FileUpload {

  private MultipartFile file;

  public MultipartFile getFile() { return file; }
  public void setFile(MultipartFile file) { this.file = file; }

}

So that covers uploading attachments, now let’s take a look at how we download attachments.  Here is the code for our DownloadAttachmentController:

public class DownloadAttachmentController extends SimpleSuccessFailureController {

  private static final int IO_BUFFER_SIZE = 1;

  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    MLog.log("DownloadAttachmentController", "Entering handleRequest()");
    // get parameters from request
    String xTasknumber = request.getParameter("x_tasknumber");
    String xFile = request.getParameter("x_file");
    String xVersion = request.getParameter("x_version");

    MLog.log("DownloadAttachmentController", "Attachment details: tasknumber=" + xTasknumber +
             " file=" + xFile + " version=" + xVersion);

    // TODO move this BPM API specific stuff out of the controller into the domain
    IWorkflowContext ctx = ContextCache.getContextCache().get(request.getRemoteUser());

    // get the task id
    String taskid = MTaskList.getTaskIdFromNumber(xTasknumber, request.getRemoteUser());

    // get the inputStream from the attachment
    InputStream in = WorkflowAttachmentUtil.getAttachment(
        ctx,                                                         // context
        MTaskList.getServerURL(),                                    // SOA URL
        taskid,                                                      // task id
        Integer.parseInt(xVersion),                                  // attachment version
        xFile,                                                       // file name
        null);                                                       // logger

    // set up the response
    response.setContentType("application/octet-stream");             // file.getContentType()
    response.setHeader("Content-Disposition", "attachment; filename=" + xFile);

    ServletOutputStream out = response.getOutputStream();
    copy(in, out);
    out.flush();
    out.close();
    return null;
  }

  private static void copy(InputStream in, OutputStream out) throws IOException {
    byte[] b = new byte[IO_BUFFER_SIZE];
    int read;
    while ((read = in.read(b)) != -1) {
      out.write(b, 0, read);
    }
  }

}

I have been a bit naughty here and broken my own separation rule.  I have some BPM API specific code in the controller here.  I will move that into the domain package where it belongs, but for now, let’s take a look at how it works.

It is similar to many of the previous examples.  We extract the parameters from the request object, then we get the context and lookup the taskId, we have seen all of this before, so I will not cover it again.  Next, we need to call the WorkflowAttachmentUtil.getAttachment() method.  This WorkflowAttachmentUtil is a helper class that we can use to get the attachment.  This gives us the binary data from the attachment in an InputStream.  This API may change in the future.

Now we use another little Spring trick to send this to the browser.  Here is that part of the code again.  Basically, we are setting the content type in the response manually to application/octet-stream which means any kind of binary data.  Then we set a Content-Disposition header that tells the browser it needs to download this attachment and what the filename is.  Most browsers will work out how to do the right thing based on this information.  Next we just copy the binary data from the InputStream straight into the ServletOutputStream and return null.

    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment; filename=" + xFile);

    ServletOutputStream out = response.getOutputStream();
    copy(in, out);
    out.flush();
    out.close();
    return null;

Finally, let’s take a look at the taskdetail.jsp view which invokes all of this functionality.  Here is the appropriate section of that view:

        <h2 class="td-h2">Attachments</h2>
        <table width="50%" class="tl-table">
          <tr>
            <th class="tl-head" width="150">User</th>
            <th class="tl-head" width="200">Date</th>
            <th class="tl-head">Name</th>
          </tr>
        </table>
        <table width="50%">
          <c:forEach items="${model.task.attachments}" var="attachment">
            <tr>
              <td class="tl-row" width="150">${attachment.updatedBy}</td>
              <td class="tl-row" width="200">${attachment.updatedDate.time}</td>
              <td class="tl-row">
                <a href="downloadattachment.do?x_tasknumber=${model.task.number}&x_file=${attachment.name}&x_version=${attachment.version}"
                   target="_blank">${attachment.name}</a>
              </td>
            </tr>
          </c:forEach>
          <tr><td>Add a new attachment:</td></tr>
          <tr>
            <td colspan="3"><form action="addattachment.do" method="POST" enctype="multipart/form-data">
              <input type="file" name="file"/>
              <input type="hidden" name="x_tasknumber" value="${model.task.number}"/>
              <input type="submit" value="Add Attachment"/>
            </form></td>
          </tr>
        </table>

The first part, which handles the download is pretty straight forward and very similar to previous examples we have seen, so I wont go into detail.  Note though that the download link points to downloadattachment.do and has a target=”_blank” so that the browser will perform the download in a new tab/window and leave the one running our application alone.

The second part is the upload.  The interesting part here is the form tag.  Note that we specify enctype=”multipart/form-data” and also, and most importantly, that the input of type=”file” is named file, which matches the name of the property in the FileUpload class.  This is the one that will create the file upload functionality for us.

<form action="addattachment.do" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>

Well, that’s it for attachments, for now at least.  Enjoy!

Posted in Uncategorized | Tagged , , | Leave a comment

Adding Process Instance Audit Images to the Worklist

I have just uploaded another update for the custom BPM worklist sample, the second one this week!  This update adds the ‘process instance audit image’ to the ‘process instance detail’ page.  That means the a picture of the actual process model with the path that this instance of the process has followed (so far) highlighted in green.

Here are a couple of examples so you can see what I mean:

And another:

As always, you can download the source code from Subversion and the Javadoc and WARs from the main worklist page.

This update adds some interesting new API usage, which we will discuss in this article.  It also requires the addition of some more libraries into your Maven repository to allow you to build with the necessary dependencies.  There are getting to be enough of these now, that I thought it was time to write a script to save you some work.  You can download the script from here (for Windows) or here (Unix-like).  Warning:  The scripts are not ‘thoroughly tested’ so please check the results carefully.  Please leave a comment if you have problems with them.

You will also need one more JAR file that I am making available for download here.  This file contains a utility class that you will need to use to get the image.  We do not currently have a working API in the product (i.e. as of 11.1.1.5) to get the process instance audit image.  This JAR file contains a utility class from Oracle that will allow you to get the image, and its API is fairly similar to what we have planned for the real API we plan to introduce in a future release.  Hopefully this will minimise any changes you need to make in the future should we release a real API.

Please note, as for the worklist as a whole, this JAR file is a sample, that is provided “as is” without any warranty or support.

This JAR file contains a class called oracle.bpm.example.util.ProcessDiagramUtil.  This class has a constructor that takes the BPMServiceClient which we have seen before, and a method called InputStream getProcessAuditDiagram(IBPMContext ctx, String instanceId) which retrieves the actual image in PNG format.

Here is the basic usage of this utility class:

  // get the utility class which will retrieve the image for us
  ProcessDiagramUtil pdu = new ProcessDiagramUtil(
      getBPMServiceClientFactory().getBPMServiceClient());

  // get the image for the requested instance
  InputStream auditDiagramData = pdu.getProcessAuditDiagram(
      (IBPMContext) ContextCache.getContextCache().get(user), instanceId);

We need to create an instance of the class, passing the constructor the BPMServiceClient.  Then we are able to get the instance audit image (in PNG format) as an InputStream.

The controller in this case is also a little different, so it is presented here.  It is just returning the PNG image, without a ‘page’ wrapped around it, so the intention is that it is called from and img tag, not from the browser URL.  We will see this later in the view.

Here is the controller code:

package com.oracle.ateam;

import org.springframework.web.servlet.ModelAndView;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;

import oracle.bpel.services.workflow.client.util.WorkflowAttachmentUtil;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

import com.oracle.ateam.util.MLog;
import com.oracle.ateam.domain.ContextCache;
import com.oracle.ateam.domain.MTaskList;

/**
    A Controller that will display the grpahical audit image for a given process instance.
    This controller should only be called in an 'img' tag - not the main application window.
 */
public class AuditImageController extends SimpleSuccessFailureController {

  private static final int IO_BUFFER_SIZE = 1;

  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    MLog.log("AuditImageController", "Entering handleRequest()");
    // get parameters from request
    String xProcessId = request.getParameter("x_processid");

    InputStream image = null;

    try {
      image = MTaskList.getAuditImage(request.getUserPrincipal().getName(), xProcessId);
      if (image == null) {
        MLog.log("AuditImageController", "image is null");
        return null;
      }
    } catch (Exception e) {
      MLog.log("AuditImageController", "Got an exception in the call to MTaskList.getAuditImage(), bailing out...");
      e.printStackTrace();
      return null;
    }

    // set up the response
    response.setContentType("image/png");

    ServletOutputStream out = response.getOutputStream();
    copy(image, out);
    out.flush();
    out.close();
    return null;
  }

  private static void copy(InputStream in, OutputStream out) throws IOException {
    byte[] b = new byte[IO_BUFFER_SIZE];
    int read;
    while ((read = in.read(b)) != -1) {
      out.write(b, 0, read);
    }
  }

}

This controller uses a similar approach to the one we used in the DownloadAttachmentController.  We set the ContentType of the HttpServletResponse and then write the image data directly into the ServletOutputStream and return null from the controller.  This is how we deliver binary data to the browser in the Spring Web MVC framework.

Here is the part of the view that calls this controller:

 <h2 class="td-h2">Process Audit Image for this Instance</h2>
  <img src="auditimage.do?x_processid=<c:out value="${model.instance.systemAttributes.processInstanceId}"/>"  />

Good luck and enjoy!

Posted in Uncategorized | Tagged , , | 12 Comments

Process Instance List and Detail added to Worklist

I have just uploaded an update to the custom BPM worklist sample which adds some new functionality around process instances, including the ability to list process instances:

And also view details for a process instance, including a list of the tasks that are part of that process instance:

The process instance list is currently hardcoded to display open (running) instances only.  I am working on adding the filtering and also a graphical process map – stay tuned for another update soon!

You can access the source code, deployable WAR files and Javadoc on the main worklist page.

This update introduces some interesting new API usage.  The view and controller are much the same as the others, so I will not cover them explicity here.  You can take a look at the source code to see how they work.  Let’s look at the new methods in the MTaskList domain object that support process instance queries.

First, here is the queryInstances method, which will retrieve a list of process instances.  As always, there are more comments in the code in Subversion.

  public static List<IProcessInstance> queryInstances(String user, String state) {
    MLog.log("MTaskList", "Entering queryInstances()");

    try {
      List<Column> displayColumns = new ArrayList<Column>();
      displayColumns.add(IColumnConstants.PROCESS_ID_COLUMN);
      displayColumns.add(IColumnConstants.PROCESS_PROCESSNAME_COLUMN);
      displayColumns.add(IColumnConstants.PROCESS_STATE_COLUMN);
      displayColumns.add(IColumnConstants.PROCESS_TITLE_COLUMN);
      displayColumns.add(IColumnConstants.PROCESS_CREATOR_COLUMN);
      displayColumns.add(IColumnConstants.PROCESS_CREATEDDATE_COLUMN);

      IInstanceQueryInput input = new InstanceQueryInput();
      input.addState(IInstanceQueryInput.PROCESS_STATE_OPEN);
      input.setAssignmentFilter(IInstanceQueryInput.AssignmentFilter.ALL);

      return getBPMServiceClientFactory()
        .getBPMServiceClient()
        .getInstanceQueryService()
        .queryInstances(
            (IBPMContext) ContextCache.getContextCache().get(user),
            displayColumns,
            null,
            null,
            input);

    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

This method shows the use of the InstanceQueryService.queryInstances() API.  The two nulls are for an optional predicate and ordering (sorting).   The code in Subversion has a list of all of the available columns that you can query .  As we did for tasks, we are just querying for the information we need to draw the instance list page, to minimise the load we put on the server.

Here is the method we use to get instance details:


  public static IProcessInstance getInstanceDetails(String user, String instanceId) {
    MLog.log("MTaskList", "Entering getInstanceDetails()");
    MLog.log("MTaskList", "Got request for instanceId " + instanceId);

    try {

      return getBPMServiceClientFactory()
        .getBPMServiceClient()
        .getInstanceQueryService()
        .getProcessInstance(
            (IBPMContext) ContextCache.getContextCache().get(user),
            instanceId);

    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

This method is fairly simple and calls the InstanceQueryService.getProcessInstance() API.  I have not wrapped the process instance like I wrapped the Task yet, but I am planning to do that to simplify the view design.  If you look in the view (instancedetail.jsp) you will see that there are a variety of child objects that we need to go to in order to get the information we want.

Here is a method that will obtain a list of tasks for a given process instance:


  public static List<MTask> findTasksForProcess(String user, String processInstanceId) {
    MLog.log("MTaskList", "Entering findTasksForProcess()");

    try {
      Predicate instancePredicate = new Predicate(TableConstants.WFTASK_INSTANCEID_COLUMN, Predicate.OP_EQ, processInstanceId);
      return getMTasks(user, instancePredicate);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

And this one will find the process instance that owns a given task:


  public static String getProcessInstanceId(String user, String taskNumber) {
    MLog.log("MTaskList", "Entering getProcessInstanceId()");

    try {

      // retrieve context
      ctx = ContextCache.getContextCache().get(user);

      // get task details
      Task task = getTaskQueryService().getTaskDetailsByNumber(ctx, Integer.parseInt(taskNumber));

      // return the process instanceId
      ProcessType processInfo = task.getProcessInfo();
      if(processInfo != null) {
        return processInfo.getInstanceId();
      }
      MLog.log("MTaskList", "Leaving getProcessInstanceId()");

    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

To allow retrieving the tasks for a given process, I made some small changes to my MTaskList.getMTasks() method.  I added a new method signature to allow me to call the method using a Predicate instead of the filter and state, and I updated the method to set default values for filter and state that make sense when we are doing a query for all tasks in a process.  Here is the changed code:


  public static List<MTask> getMTasks(String user, String filter, String state) {
    return getMTasks(user, filter, state, null);
  }

  private static List<MTask> getMTasks(String user, Predicate predicate) {
    return getMTasks(user, null, null, predicate);
  }

  private static List<MTask> getMTasks(String user, String filter, String state, Predicate predicate) {

    MLog.log("MTaskList", "Entering getMTasks()");

    try {

      if (filter == null) filter = "megroup";
      if (state == null) state = "any"

      //    ... continues as before ...

I also had to add two new dependencies in order to use the new APIs that are in this version.

The two JAR files are:

  • Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.model.jar
  • Oracle_SOA1\soa\modules\oracle.bpm.runtime_11.1.1\oracle.bpm.core.jar

I added these to Maven with the following coordinates respectively:

  • com.oracle.soa:bpmproject_model:11.1.1.5
  • com.oralce.soa:bpmcore:11.1.1.5

And of course I added them to my pom, as follows:


       <dependency>
            <groupId>com.oracle.soa</groupId>
            <artifactId>bpmproject_model</artifactId>
            <version>11.1.1.5</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.oracle.soa</groupId>
            <artifactId>bpmcore</artifactId>
            <version>11.1.1.5</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

Enjoy!

Posted in Uncategorized | Tagged , , | Leave a comment

Introducing Oracle Middleware into your environment

This article is intended for organisations that have just recently made a decision to adopt the  Oracle Fusion Middleware stack. It can be a very exciting and nerve-wracking time. You have probably done a lot of research on the technology and have a good idea where each particular product/module will fit into your environment. You are also likely to have some projects in the pipeline on which you are planning to utilise the products. The question is: how do you take full advantage of the products to achieve the best possible outcome and fast forward though the unnecessary hurdles.

Having chosen Oracle Fusion Middleware, you most likely understand of the benefits using the ‘Service Oriented Architecture’ approach.  The main principles of SOA are to promote reusability, consistency, governance and business agility. Even if you do not have a particular project and deliverables in mind, you still should think about how to ensure you follow SOA principles.

It is important to understand the distinction between Service Oriented Architecture and Oracle Fusion Middleware product set. Service Oriented Architecture is a philosophy/methodology, a collection of best practices and approaches to software development.  Oracle Fusion Middleware products enable you to follow/implement a Service Oriented Architecture. It is your responsibility to ensure the best strategy for your organisation to build a SOA and utilise the products to their full potential.

Where do you start?  Let’s explore a few areas where  you might  start…

Many experts and analysts strongly promote the importance of organisations developing (or adopting) a Reference Architecture before embarking on a particular project development.  A Reference Architecture is a collection of documents and code samples that outlines standards, recommendations, approaches and examples for achieving various outcomes.  The purpose of the Reference Architectures is to achieve common approaches and standards thoughout the organisation. The advantages of adopting and enforcing compliance to a Reference Architecture is obvious to many large organisation with multiple internal and external IT/development groups, applications and lines of businesses involved in IT projects. It is however, equally important for smaller organisations to have a Reference Architecture, as external consultants and/or partners may be involved in projects and you need to establish standards and governance.

Your Reference Architecture should be comprehensive to cover various areas of your business, and it is up to you to define these areas. Common areas seen in Reference Architectures are process, service definition, security, monitoring and management, content management, portals and user interfaces, data models, build/release management, and so on… You probably already have some rules, documentation, and knowledge in place for some or even all of these areas. You could also look for existing Reference Architectures designed for your industry, like TMF for telecommunications, or cross-industry ones like TOGAF or Zachman. You should never start from scratch. These concepts are not new and there is no point reinventing the wheel.

A Reference Architecture would normally consist of layers:

  1. The Theoretical Layer, which gives high level architectural guidance or patterns (e.g.  follow SOA principles, build for reuse, manage your assets, separate security policy from the codebase, etc.)
  2. The Descriptive Layer, which gives more detail about each pattern, but is still agnostic of the implementation technology (e.g.  use canonical data models, expose services as web services, use policy-based security enforcement system, etc.)
  3. The Prescriptive Layer, which tells you exactly what tools and methods to use to implement various patterns (e.g. to create a service use Oracle Service Bus, to create canonical data models use XSDs, store your canonical data model in MDS, user SOAP 1.2 web services, use WS-Security or SAML for all services, etc.)
  4. The Reference Layer, which provides fully worked examples for each pattern (e.g. a working example of a service that looks up a price for a product from E-Business Suite, implemented in Oracle Services Bus, using security policies from OWSM, a canonical data model that defines customer, product, etc.)

Even the best defined Reference Architecture would be of little value if it is not being followed. This leads us to governance.

IT governance, like any type of governance, is very hard to quantify when you are considering ROI. But the importance of it is unquestionable if you want to be a true SOA practitioner. You need to have Enterprise Architect(s), or someone who acts as an enterprise architect in your organisation, to ensure compliance, monitor reuse, and carry out reviews, approvals and other general governance. Architects have to ensure that no project is able to go live unless it is compliant with your Reference Architecture principles (or there is a really good reason why it is not). Among other things, they develop processes, manage assets and their relationships, ensure reuse, analyse impact of proposed changes and record usage. Very often, architects uses tools to manage the information needed to successfully govern a SOA environment. Oracle Service Registry and Oracle Enterprise Repository are two such tools.

The other aspect to keep in mind is that as with any change you should expect and plan for resistance. It is human nature to fall back to what is comfortable and familiar. SOA will probably become comfortable in the long run, but familiarity will take time. Proper training and time for learning, acceptance and organizational change must be allocated.

Following our previous statement about not reinventing a wheel, it would be advisable to find mentors to help you through the initial steps of adopting SOA in your organisation. Here are just a few ideas of getting proper help you need:

  1. Hire someone who has done it before successfully,
  2. Follow online resources, blogs, articles, etc.,
  3. Attend conferences, user groups, related event organised by Oracle or partner (many of them are free), and
  4. Establish a good working relationship with your Oracle account manager who could quickly pull in the needed resources.

And last but not least, remember that with flexibility comes complexity. If you are new to Oracle Fusion Middleware products, we recommend adopting a three-step approach:

  • The first time, just try to get things to work any way you can, don’t worry about anything else, just make it do what you want it to do. For example, using SOA Suite connect to E-Business Suite through Integrated SOA Gateway, pull out some data and display it on a webpage using ADF. The first time you do a SOA project, you should consider it a throwaway project.  The purpose of this attempt is to get familiar with the tools, nothing more.
  • Now that you know some of the terminology and have some familiarity with the tools, read some best practices material about how to design a SOA application.  Now have a second go at it. This time try to follow the best practices, develop a canonical data layer, perhaps you will make different design decision, improve your approach the best way you can.  The purpose is to learn about how to design a SOA application, when to use each tool in your toolkit.
  • The third time, do some more reading and start thinking about issues like team development, monitoring, management, security and performance.  The purpose of this attempt is to understand how these cross cutting concerns affect your SOA design and development practices.

The reason we recommend this approach is that we have found it is generally too hard for a person to learn and put in to practice all of these new concepts  at the same time that they are learning the new technology. Give yourself time, play with the tools and enjoy the ride.

Posted in Uncategorized | Tagged | Leave a comment

Continuous Integration with SCA Tests

This post builds on the earlier posts in this series which showed an approach to getting started with continuous integration for Oracle SOA and BPM.  In this post, we will look at how to add SCA Tests to our continuous integration environment – executing them as part of the build, and reporting on them in the continuous integration server.

Let’s start by building a very simple composite with a test, then we will get that working in our continuous integration environment.  This sample is built using JDeveloper 11.1.1.5 and we will be deploying to Oracle SOA Suite 11.1.1.5.  In this case, all running on Windows 2008 Server R2 and everything is 64-bit.

In JDeveloper, select New from the File menu to create a new application.  Choose Applications under General on the left hand side and then SOA Application from the list of templates, then click on OK.  Enter a name for your application.  I called mine SCATestSample:

Click on Next and enter a name for your SOA project, I gave mine the same name.  Then click on Finish.

When the composite opens, drag a BPEL Process out of the palette and drop it on the composite.  I used a BPEL 2.0 process.  Give it a name, I called mine SCATestProcess and click on OK to create it.

Now, let’s set up our inputs and outputs.  We are going to pass two numbers into the process and have it return the sum of those numbers.  Open up the xsd folder in the project navigator on the left hand side and find the file called SCATestProcess.xsd.  Yours might have a different name if you chose to give your process a different name, but the first part should match the name of your process.

Switch to the source view using the little tab at the bottom left of the main editor pane.

Here is the code to put in this file:


<?xml version="1.0" encoding="UTF-8"?>
<schema attributeFormDefault="unqualified" elementFormDefault="qualified"
        targetNamespace="http://xmlns.oracle.com/SCATestSample/SCATestSample/SCATestProcess"
        xmlns="http://www.w3.org/2001/XMLSchema">
 <element name="process">
  <complexType>
   <sequence>
    <element name="input1" type="int"/>
    <element name="input2" type="int"/>
   </sequence>
  </complexType>
 </element>
 <element name="processResponse">
  <complexType>
   <sequence>
    <element name="result" type="int"/>
   </sequence>
  </complexType>
 </element>
</schema>

Go back to your composte and open up your process by double clicking on it.  Drag an Assign activity from the palette into your process.  Drop it in the middle, between the receiveInput and the callbackClient.

Double click on your assign to open its settings.  On the right hand side, open up the tree so that you can see the output client:result as shown below and then drag the little calculator icon and drop it on the client:result.  This will let us enter a ‘formula’ to set the value of the output.

Enter the formula as shown.  You can do this by expanding the inputVariable on the left hand side and then clicking on the Insert Into Expression button, or you can just type it in.  Note that the names may be different if you chose different names for your variables when you created your XSD.


$inputVariable.payload/client:input1 + $inputVariable.payload/client:input2

Click on OK and OK again.  Then save your project.

Now we are ready to set up the SCA Test.  We will create a simple test that passes in 2 and 2 and checks that the output is 4.

To create the test, we return to the composite, and then go to the Structure pane.  It is usually in the bottom left.  If you cannot see it, you can open it from the View menu.  When you have it, right click on the Test Suites item and choose Create Test Suite… from the popup context menu.  Enter a name for your test suite, I called mine TestSuite1, and for your test, I called mine Test1.

The test will open in the main editor window.  Let’s set up the inputs.  Right click on the ‘exposed service’ in the left hand yellow lane.  Choose Edit Initiate Messages from the popup menu.  Manke sure you have payload selected in the Part and then click on Generate Sample to create a message.  Now just go and edit the data so that both inputs are set to ‘2’ as shown below.  Then click on OK.

Now we will set up a test on the output.  Right click on the wire between the ‘exposed service’ and the process.  Choose Edit Wire Actions from the popup menu.  Make sure you are in the Asserts tab and click on the green plus icon to add a new assertion (i.e. something we want this test to check is true.)

Select the Assert Callback option at the top, then make sure you have processResponse as the Callback Operation.  Click on the Generate Sample button and change the result to ‘4’ as shown below.  Then click on OK and OK again, and then save your project.

Now we have a simple process that will add the two numbers we input and return the result, and a simple test case that will check that it works.

Let’s now get this ready to build this with Hudson.  We are going to take a slightly different approach than the earlier samples.  Instead of using the ‘Maven’ job type, we are going to use ‘freestyle.’  This is so that we can tell Hudson where to pick up the test results for reporting.

First, let’s set up our POM.  As in the earlier examples, create a new file called pom.xml in your project and add the following content to it.  Note that you will have to change this to suit your environment, specfically the SCM information and that path names to things in C:/Oracle/Middleware if you installed in a different location, or you are not using Windows.

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>ScaTestSample</groupId>
  <artifactId>ScaTestSample</artifactId>
  <version>1.0-SNAPSHOT</version>

  <scm>
    <connection>scm:svn:https://administrator@bpm.mark.oracle.com/svn/ScaTestSample/trunk</connection>
    <developerConnection>scm:svn:https://administrator@bpm.mark.oracle.com/svn/ScaTestSample/trunk</developerConnection>
  </scm>

  <dependencies>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.6</version>
        <executions>
          <execution>
            <id>sca-compile</id>
            <phase>compile</phase>
            <configuration>
              <target>
                <property name="scac.input" value="${basedir}/composite.xml" />
                <property name="scac.application.home" value="${basedir}/.." />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-compile.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="scac" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
          <execution>
            <id>sca-package</id>
            <phase>package</phase>
            <configuration>
              <target>
                <property name="build.compiler" value="extJavac"/>
                <property name="compositeName" value="${project.artifactId}" />
                <property name="compositeDir" value="${basedir}" />
                <property name="revision" value="${project.version}" />
                <property name="scac.application.home" value="${basedir}/.." />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-package.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="package" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
          <execution>
            <id>sca-deploy</id>
            <phase>deploy</phase>
            <configuration>
              <target>
                <property name="serverURL" value="http://bpm.mark.oracle.com:7001" />
                <property name="user" value="weblogic" />
                <property name="password" value="welcome1" />
                <property name="sarLocation" value="${basedir}/deploy/sca_${project.artifactId}_rev${project.version}.jar" />
                <property name="overwrite" value="true" />
                <property name="forceDefault" value="true" />
                <property name="partition" value="default" />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-deploy.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="deploy" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
           <execution>
            <id>sca-test</id>
            <phase>deploy</phase>
            <configuration>
              <target>
                <property name="jndi.properties.input" value="c:/Oracle/Middleware/sca-test.jndi.properties" />
                <property name="scatest.input" value="SCATestSample" />
                <property name="scatest.format" value="junit" />
                <property name="scatest.result" value="reports" />
                <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-test.xml"
                     dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
                     target="test" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

  <distributionManagement>
    <!-- use the following if you're not using a snapshot version. -->
    <repository>
      <id>local</id>
      <name>local repository</name>
      <url>file:///c:/users/administrator/.m2/repository</url>
    </repository>
    <!-- use the following if you ARE using a snapshot version. -->
    <snapshotRepository>
      <id>localSnapshot</id>
      <name>local snapshot repository</name>
      <url>file:///c:/users/administrator/.m2/repository</url>
    </snapshotRepository>
  </distributionManagement>

</project>

This POM has a new execution section added that we did not see in the previous examples.  This new section is to run the test suites.  Here it is:

<execution>
  <id>sca-test</id>
  <phase>deploy</phase>
  <configuration>
    <target>
      <property name="jndi.properties.input" value="c:/Oracle/Middleware/sca-test.jndi.properties" />
      <property name="scatest.input" value="SCATestSample" />
      <property name="scatest.format" value="junit" />
      <property name="scatest.result" value="reports" />
      <ant antfile="c:/Oracle/Middleware/Oracle_SOA1/bin/ant-sca-test.xml"
           dir="c:/Oracle/Middleware/Oracle_SOA1/bin"
           target="test" />
    </target>
  </configuration>
  <goals>
    <goal>run</goal>
  </goals>
</execution>

There are a couple of interesting things to note in this section.  First, the SCA Test task expects to be provided with a jndi.properties file that contains details about how to access the server.  We will create this in a moment.  You can see that we have a property named jndi.properties.input which points to the location of this file.

The scatest.input property has to be the name of the composite that you want to test.  It has to match exactly.  The scatest.format property, which we set to junit, tells SCA Test to provide the details on the test execution in JUnit format.  Hudson has built-in support for reading and interpreting JUnit reports.  Finally, the scatest.result property tells SCA Test where to save the reports.  We have set this to reports.  This is relative to the project root, we will see this later when we configure the job.  SCA Test will create several XML files in this location.

Now we need to check our project into Subversion.  If you have not done so already, create a new Subversion repository for this project, and add a Subversion connection in JDeveloper.  You can do this in the Versioning Navigator by right clicking on the Subversion item and selecting New Repository Connection from the popup menu.  If you don’t see the Versioning Navigator, you can open it from the View menu, its under Team.

Now we can add our project to version control.  Right click on the project in the project navigator on the left and select Version Project form the popup menu.  Choose the correct repository and location, as shown below, then click on Next.  Enter a comment if you want to and then complete the wizard.

We will also need to create that jndi.properties file that we discussed earlier.  It needs to be in the location you specified in your POM.  Here is the content, you will need to change this to suit your environment:


java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory
java.naming.provider.url=t3://bpm.mark.oracle.com:7001/soa-infra
java.naming.security.principal=weblogic
java.naming.security.credentials=welcome1
dedicated.connection=true
dedicated.rmicontext=true

Now we will tell Hudson how to build the project and run the tests.  Log in to Hudson and create a new job.  Give it a name, I called mine ScaTestSample.  Select the option to Build a free-style software project.  Note that this is different to the earlier examples.  Then click on OK.

In the Source Code Management section, select Subversion and enter the repository URL.

Check the Poll SCM option and enter a schedule.   I used ‘* * * * *’ (thats five asterisks separated by spaces) which means check every minute.

In the Build section, click on the Add build step button and choose Invoke top-level Maven targets.  Enter clean deploy as your Goals and set the POM to pom.xml.

In the Post-build Actions section, check the option to Publish JUnit test result report and enter the location of reports (in Your report XMLs) as reports/*.xml.  This tells Hudson to look for XML files in the reports directory.  Recall that we configured this in our POM earlier.

Save your new job.  Run it manually, or wait for the scheduler to run it.  After it completes, you can navigate into the build and click on the Latest Test Results link to view the outcome of the tests.  You should find your test suite and all of your tests listed here, along with the results.  There will only be one in our case, as we only defined one, but of course in a real project, you would probably define several test suites with many tests in each.

If you run the build a few more times, you will see that a Test Result Trend graph is automatically added to the job’s main page.

For completeness, here is the output from my job.  You can see the compile, package, deploy and test phases in here.

Started by an SCM change
Updating https://bpm.mark.oracle.com/svn/ScaTestSample/trunk revision: Jun 24, 2011 3:42:21 PM depth:infinity ignoreExternals: false
U         pom.xml
At revision 6
[workspace] $ mvn.bat -f pom.xml clean deploy
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - ScaTestSample:ScaTestSample:jar:1.0-SNAPSHOT
[INFO]    task-segment: [clean, deploy]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting directory C:\hudson\jobs\ScaTestSample\workspace\target
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\hudson\jobs\ScaTestSample\workspace\src\main\resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] No sources to compile
[INFO] [antrun:run {execution: sca-compile}]
[INFO] Executing tasks

main:

scac:
Validating composite "C:\hudson\jobs\ScaTestSample\workspace/composite.xml"
     [scac] BPEL 2.0 validation of "SCATestProcess" took 153.9 milliseconds
     [scac] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
     [scac] >> modified xmlbean locale class in use
     [scac] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[INFO] Executed tasks
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\hudson\jobs\ScaTestSample\workspace\src\test\resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[INFO] [jar:jar {execution: default-jar}]
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: C:\hudson\jobs\ScaTestSample\workspace\target\ScaTestSample-1.0-SNAPSHOT.jar
[INFO] [antrun:run {execution: sca-package}]
[INFO] Executing tasks

main:
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..
    [input] skipping input as property compositeDir has already been set.
    [input] skipping input as property compositeName has already been set.
    [input] skipping input as property revision has already been set.

clean:
     [echo] deleting C:\hudson\jobs\ScaTestSample\workspace/deploy/sca_ScaTestSample_rev1.0-SNAPSHOT.jar
   [delete] Deleting: C:\hudson\jobs\ScaTestSample\workspace\deploy\sca_ScaTestSample_rev1.0-SNAPSHOT.jar

init:

scac-validate:
     [echo] Running scac-validate in C:\hudson\jobs\ScaTestSample\workspace/composite.xml
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..
    [input] skipping input as property compositeDir has already been set.
    [input] skipping input as property compositeName has already been set.
    [input] skipping input as property revision has already been set.

scac:
Validating composite "C:\hudson\jobs\ScaTestSample\workspace/composite.xml"
     [scac] BPEL 2.0 validation of "SCATestProcess" took 154.4 milliseconds
     [scac] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
     [scac] >> modified xmlbean locale class in use
     [scac] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

package:
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..
    [input] skipping input as property compositeDir has already been set.
    [input] skipping input as property compositeName has already been set.
    [input] skipping input as property revision has already been set.

compile-source:
    [mkdir] Created dir: C:\hudson\jobs\ScaTestSample\workspace\dist
     [copy] Copying 29 files to C:\hudson\jobs\ScaTestSample\workspace\dist
     [copy] Warning: C:\hudson\jobs\ScaTestSample\.adf does not exist.
     [copy] Warning: C:\hudson\jobs\ScaTestSample\src does not exist.
     [copy] Warning: C:\hudson\jobs\ScaTestSample\workspace\src does not exist.
      [jar] Building jar: C:\hudson\jobs\ScaTestSample\workspace\deploy\sca_ScaTestSample_rev1.0-SNAPSHOT.jar
   [delete] Deleting directory C:\hudson\jobs\ScaTestSample\workspace\dist
[INFO] Executed tasks
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\hudson\jobs\ScaTestSample\workspace\target\ScaTestSample-1.0-SNAPSHOT.jar to C:\.m2\repository\ScaTestSample\ScaTestSample\1.0-SNAPSHOT\ScaTestSample-1.0-SNAPSHOT.jar
[INFO] [deploy:deploy {execution: default-deploy}]
[INFO] Retrieving previous build number from localSnapshot
Uploading: file:///c:/users/administrator/.m2/repository/ScaTestSample/ScaTestSample/1.0-SNAPSHOT/ScaTestSample-1.0-20110624.054233-2.jar
2/2K
2K uploaded  (ScaTestSample-1.0-20110624.054233-2.jar)
[INFO] Uploading project information for ScaTestSample 1.0-20110624.054233-2
[INFO] Retrieving previous metadata from localSnapshot
[INFO] Uploading repository metadata for: 'snapshot ScaTestSample:ScaTestSample:1.0-SNAPSHOT'
[INFO] Retrieving previous metadata from localSnapshot
[INFO] Uploading repository metadata for: 'artifact ScaTestSample:ScaTestSample'
[INFO] [antrun:run {execution: sca-deploy}]
[INFO] Executing tasks

main:
     [echo] oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..

deploy:
    [input] skipping input as property serverURL has already been set.
    [input] skipping input as property sarLocation has already been set.
setting user/password..., user=weblogic
Processing sar=C:\hudson\jobs\ScaTestSample\workspace/deploy/sca_ScaTestSample_rev1.0-SNAPSHOT.jar
Adding sar file - C:\hudson\jobs\ScaTestSample\workspace\deploy\sca_ScaTestSample_rev1.0-SNAPSHOT.jar
INFO: Creating HTTP connection to host:bpm.mark.oracle.com, port:7001
INFO: Received HTTP response from the server, response code=200
---->Deploying composite success.
[INFO] Executed tasks
[INFO] [antrun:run {execution: sca-test}]
[INFO] Executing tasks

main:
     [echo] Running scatest using oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/..

test:
     [echo] Classpth = c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-ext.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\oracle-soa-client-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-infra-mgmt.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel-common.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel.jar;c:\Oracle\Middleware\wlserver_10.3\server\lib\weblogic.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-api.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-common.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-internal.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jrf_11.1.1\jrf-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-client-stubs-was.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.ejb.thinclient_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.orb_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\plugins\com.ibm.ws.runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.admin.client_7.0.0.jar
     [echo] Running scatest using oracle.home = c:\Oracle\Middleware\Oracle_SOA1\bin/.. SCATestSample
     [echo] Using context = build.properties
     [echo] Using path = c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-ext.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\fabric-runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\oracle-soa-client-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-infra-mgmt.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel-common.jar;c:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.bpel_11.1.1\orabpel.jar;c:\Oracle\Middleware\wlserver_10.3\server\lib\weblogic.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-api.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-common.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jps_11.1.1\jps-internal.jar;c:\Oracle\Middleware\oracle_common\modules\oracle.jrf_11.1.1\jrf-api.jar;c:\Oracle\Middleware\oracle_common\soa\modules\oracle.soa.mgmt_11.1.1\soa-client-stubs-was.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.ejb.thinclient_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.orb_7.0.0.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\plugins\com.ibm.ws.runtime.jar;c:\Oracle\Middleware\Oracle_SOA1\bin\${was.home}\runtimes\com.ibm.ws.admin.client_7.0.0.jar
    [input] skipping input as property scatest.input has already been set.
    [input] skipping input as property jndi.properties.input has already been set.
  [scatest] Junit formatting
  [scatest] <testsuite name="sca.default-SCATestSample.TestSuite1" tests="1" errors="0" failures="0" time="0.241"><properties><property name="db.type" value="oracle"/><property name="bpel.host.name" value="bpm"/><property name="soa.oracle.home" value="C:\Oracle\Middleware\Oracle_SOA1"/><property name="suite.start.date" value="2011-06-24T15:42:37.990+10:00"/><property name="suite.end.date" value="2011-06-24T15:42:38.231+10:00"/><property name="run.start.date" value="2011-06-24T15:42:37.990+10:00"/><property name="run.end.date" value="2011-06-24T15:42:38.231+10:00"/></properties><testcase name="Test1" classname="sca.default-SCATestSample.TestSuite1.Test1" time="0.241"/></testsuite>
  [scatest] C:\hudson\jobs\ScaTestSample\workspace\reports\BPEL-sca.default-SCATestSample.TestSuite1.xml
  [scatest] <testsuite name="sca.default-SCATestSample.codeCoverages" errors="0" failures="0" tests="0" time="0.0"/>
  [scatest] C:\hudson\jobs\ScaTestSample\workspace\reports\BPEL-sca.default-SCATestSample.codeCoverages.xml
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 22 seconds
[INFO] Finished at: Fri Jun 24 15:42:45 EST 2011
[INFO] Final Memory: 23M/439M
[INFO] ------------------------------------------------------------------------
Recording test results
Finished: SUCCESS

You might also want to take a look in Enterprise Manager at the instances for this composite.  You should see one or more test instances (marked with a little yellow dot) depending on how many times you ran the job.  You can drill down into these, as normal, and see the data.

One other point for consideration is the placement of the deploy and test in the Maven ‘deploy’ goal.  We consider that the ‘integration-test’ goal is possibly a more philosophically correct place to put these actions.  However, we are sticking to the ‘deploy’ goal as it seems to make more sense to us to deploy the project to the runtime in the ‘deploy’ phase – just because they have the same name, and it is possibly less confusing.  We may revisit this choice later.

Posted in Uncategorized | Tagged , , | 4 Comments

Allowing limited access to Enterprise Manager

Customers often ask about how to give certain people limited access to Enterprise Manager, often they talk about ‘read only’ access.  Enterprise Manager does in fact have three roles defined out-of-the-box that provide various levels of access.  You can see details of exactly what each can do in the documentation here.

Setting up a new user and giving them one of these limited access roles is pretty easy.

First, we need to create the new user.  If you are using an LDAP server or Active Directory, etc., then you will probably want to go and create the user in those tools.  Otherwise, you can do it from the WebLogic Server console.  Go to the Security Realms option in the navigation menu on the left, then open your realm and then the Users and Groups tab and then the Groups sub-tab.  You will see there is a group called Monitors.  We will use this one for our example.

Go to the Users sub-tab and click on the New button.  Enter a user name and password, as shown below and then click on OK.  I called my new user monitor.

Go back to the Users sub-tab again, and click on your new user in the list, then open the Groups tab for that user.  Find the Monitors group in the list of Available groups on the left, and click on the little blue right arrow to add it to the Chosen groups on the right.  Then click on Save.

That’s enough to give our new user limited access to the WebLogic console.  You can log in with that user now and try it out!

If you also want to give them limited access to the Enterprise Manager, we need to assign one more role to them.  Log on to Enterprise Manager as an administrative user.   Navigate to the WebLogic domain in the navigation tree on the left.  When the domain is displayed on the right hand side, open the menu at the top and choose Security and then Application Roles.

In the Select Application Slice to Search select soa-infra and then click on the little green arrow in a blue circle button to do a search.  You should see in the list a role called SOAMonitor.  Click on that role.

Then click on the Add User button.  In the popup dialog, search for your new user and click on the little blue arrow to move them to the Selected Users box, as shown below.  Click on OK and then OK again.

Now you can log on to Enterprise Manager using your new user.  Notice that you have limited access.  For example, on the Composite page, you only get the test option…

… whereas an administrative user gets several other options too:

So there you go, an easy way to give users limited access to the WebLogic console and Enterprise Manager.  Be sure to check the documentation link at the top of this post to see what these user are and are not able to do.

Posted in Uncategorized | Tagged , , | Leave a comment

New version of JDeveloper released with Maven support and better performance

Oracle has released a new version of JDeveloper, 11.1.2.0, with a whole bunch of new features, including Maven support, and a lot of performance enhancements.  You can check out details of the new features here.

Please note that this version does not support WebCenter, SOA and BPM development.  You should continue to use 11.1.1.x for that kind of development.

Posted in Uncategorized | Tagged , | Leave a comment

Java 7 is nearly here…

That’s right, Java 7 is just around the corner. Here’s a few of the new language features that I really like:

Match on Strings in a switch statement

No longer do we have to write those ugly case statements like the one shown below. Some people put the variable and the constant the other way around, but then you might get a NullPointerException, so you have to check for that too, which is even more ugly. I prefer to do it the way shown, but every time I type one, I can’t help feeling a little bit of distaste.

  case (&quot;something&quot;.equals(whatever));

But now, we can write these much more elegantly. Here is an example:

public class Test {
  public static void main(String[] args) {
    if (args.length != 1) {
      System.out.println(&quot;You must specify exactly one argument&quot;);
      System.exit(0);
    }
    switch (args[0]) {
      case &quot;fred&quot;:
        System.out.println(&quot;Hello fred&quot;);
        break;
      case &quot;joe&quot;:
        System.out.println(&quot;Hello joe&quot;);
        break;
      default:
        break;
    }
  }
}

Try with resources

Java 7 introduces a ‘try with resources’ construct which allows us to define our resources for a try block and have them automatically closed after the try block is completed – either successfully or when an exception occurs. This makes things a lot neater. Let’s take a look at the example below which include both the ‘old’ way and the ‘new’ way:

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;

public class Try {
  public static void main(String[] args) {
    // the old way...
    InputStream oldfile = null;
    try {
      oldfile = new FileInputStream(new File(&quot;myfile.txt&quot;));
      // do something with the file
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      // we need to check if the InputSteam oldfile is still open
      try {
        if (oldfile != null) oldfile.close();
      } catch (IOException ioe) {
        // ignore
      }
    }
    // the new way...
    try (InputStream newfile = new FileInputStream(new File(&quot;myfile.txt&quot;))) {
      // do something with the file
    } catch (IOException e) {
      e.printStackTrace();
    }
    // the InputStream newfile will be closed automatically
  }
}

When we do things the old way, there are a few areas of ugliness:

  • We are forced to declare our resource in the scope outside the try so that it will still be visible in the finally,
  • We have to assign it a null value, otherwise the compiler will complain (correctly) that it may not have been initialized when we try to use it in the finally block,
  • We need to write a finally block to clean up after ourselves,
  • In our finally block, we first have to check if things worked or not, and if they did not, then we have to clean up,
  • We need another try inside the finally since the cleanup operation in the finally is usually that could throw another exception!

The new way is a lot cleaner. We simply define our resources in parentheses after the try and before the opening brace of the try code block. Multiple resources can be declared by separating them with a semicolons (;). No need to define the resource in the outer scope (unless you actually want it to be available out there).

Also, you don’t have to write a finally block at all. Any resources defined this way will automatically be released when the try block is finished. Well not any resource, but any that implement the java.lang.AutoCloseable interface. Several standard library classes, including those listed below, implement this interface.

  • java.nio.channels.FileLock
  • javax.imageio.stream.ImageInputStream
  • java.beans.XMLEncoder
  • java.beans.XMLDecoder
  • java.io.ObjectInput
  • java.io.ObjectOutput
  • javax.sound.sampled.Line
  • javax.sound.midi.Receiver
  • javax.sound.midi.Transmitter
  • javax.sound.midi.MidiDevice
  • java.util.Scanner
  • java.sql.Connection
  • java.sql.ResultSet
  • java.sql.Statement

Much nicer!

Catch multiple-type exceptions

Ever found yourself writing a whole heap of catch blocks with the same code in them, just to handle different types of exceptions? Me too! Java 7 provides a much nicer way to deal with this:

try {
  // do something
} catch (JMSException | IOException | NamingException e) {
  System.out.println(&quot;Oh no! Something bad happened);
  // do something with the exception object...
  e.printStackTrace();
}

You can list the types of exceptions you want to catch in one catch block using a pipe (|) separator. You can still have other catch blocks as well.

Type Inference

I never thought I would see the day Smile and it is still a long way away from the type inference in languages like Scala, but we can now omit types when initializing a generic collection. One small step for Java… one huge leap for mankind!

  Map&lt;String, List&lt;String&gt;&gt; stuff = new HashMap&lt;&gt;();

The ‘diamond operator’ (<>) is used for type inference. Saves a bunch of wasteful typing. However, it only works when the parameterised type is obvious from the context. So this wont compile:

List&lt;String&gt; list = new ArrayList&lt;&gt;();
list.addAll(new ArrayList&lt;&gt;());

But this will:

List&lt;String&gt; list = new ArrayList&lt;&gt;();
List&lt;? extends String&gt; list2 = new ArrayList&lt;&gt;();
list.addAll(list2);

Why? The addAll() method has a parameter of Collection<? extends String>. In the first example, since there is no context, the compiler will infer the type of new ArrayList<>() in the second line to be ArrayList<Object> and Object does not extend String.

The moral of the story: just because you might be able to use <> in a method call – probably you should try to limit its use to just variable definitions.

Nicer Numeric Literals

There are also a couple of small enhancements to numeric literals. Firstly, support for binary numbers using the ‘0b’ prefix (C programmers everywhere breathe a sigh of relief) and secondly, you can put underscores (_) between digits to make numbers more readable. That’s between digits only, nowhere else, not first, or last, or next to a decimal point or type indicator (like ‘f’, ‘l’, ‘b’, ‘x’).

Here are some examples:

// this is a valid AMEX number used for test transactions, you can't buy anything with it
// but it will pass the credit card number validation tests
long myCreditCardNumber = 3782_822463_10005L;
long myNameInHex = 0x4D_41_52_4B;
long myNameInBinary = 0b01001101_01000001_01010010_01001100;
long populationOfEarth = 6_852_472_823L;
// I glad I didn't have to count this!

New Look and Feel for Swing

There is a new look and feel for Swing (which it desperately needed!) called ‘Nimbus.’ It looks a bit like this:

image

Or this:

image

It was kinda there as a draft for a little while already in Java 6. Why is it good? It can be rendered at any resolution, and it is a lot faster (yeah!) and arguably better looking that those boring old ones!

Better parallel performance

I attended a presentation by Doug Lea a couple of days ago, and he told us proudly that Java 7 also has some of his code buried in there that has long been waiting to see the light of day. A new work stealing queue implementation in the scheduler which should provide a good boost to performance of parallel tasks. This should be especially helpful as we write more and more parallel software in these days of more cores rather than higher frequencies. Especially nice if you are writing using an easy concurrent programming model like Actors in Scala, but still good if you are struggling through threads, locks, latches and all that. Thanks Doug!

Summary

Well there you have, a few small steps forward. I am looking forward to seeing a few bigger steps in Java 8!

Posted in Uncategorized | Tagged , | Leave a comment