Tuesday, November 24, 2009

Hacking Oracle SOA Suite (11.1.1) builds to work with Ivy and Artifactory

As discussed in a few previous posts, I’m on a mission to get my project’s SOA Suite builds to work without having to rely on a local installation of JDeveloper 11g with the added SOA Extension.

I’ve continued to make progress (with the help of Mayur – thanks) and we’ve now got compilation, packaging, deployment, and Unit Testing to work. We’ve still to tidy up our scripts so that they’re ready for publishing for public consumption, but in the meantime, I thought I’d put out some snippets which will help the enthusiastic to get to where we have.

Getting your Jar dependencies into Artifactory

My aim was to depend on Maven (and JFrog Artifactory) for our dependencies as much as possible. Where we could use publicly available Jars from the standard repositories we did. Sadly this turned out to be very infrequent. It did mean however that there was another reason to download and install Artifactory. This was very simple. We downloaded the Standalone version and ran it using the embedded Jetty. The only thing I needed to change from the default settings was to set a proxy (we’re behind a firewall.)

I then had to manually add all the Oracle dependencies which I had identified to ext-releases-local (Artifactory’s default Local repository for third party libraries). I had logged in as an Admin, and using the “Deploy” tab added them one at a time.

When I’d finished, it looked like this (apologies for the crappy screen grabs):

artifactory-structure-1 

artifactory-structure-2

artifactory-structure-6 artifactory-structure-3

artifactory-structure-4

 artifactory-structure-5

NOTE: It might be nice to bundle all these up as an artifacts bundle. I’ve not had the time to do this yet.

This meant we were now ready to link Ivy up to all of this.

Connecting Ivy and Artifactory

Next we had to tell Ivy to use either its local cache, or Artifactory, for all its dependency lookups. To do this, I created a new ivysettings.xml file with the following content:

<ivysettings>
    <settings defaultResolver="chain" />
    <resolvers>
        <chain name="chain" returnFirst="true">
            <filesystem name="local">
                <ivy pattern="C:/Documents and Settings/aharmel/.m2/repository/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
            </filesystem>
            <url name="shared">
                <artifact pattern="
http://build-xp-10:8081/artifactory/repo/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
            </url>
            <url name="public">
                <artifact pattern="
http://build-xp-10:8081/artifactory/repo/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
            </url>
        </chain>
    </resolvers>
</ivysettings>

This meant that when an ivy:resolve target was called, the local Ivy cache would be looked up first, and if that was not successful, then artifactory would be checked for both shared and public lookups. This meant that we would cache those dependencies which were available publicly in Maven, speeding up the second, and all subsequent lookups.

Using Ivy to get the dependencies in the Ant files

Penultimately (is that a word? I doubt it), we created an ivy.xml file containing all our dependencies and different configurations:

<ivy-module version="2.0">
    <info organisation="example" module="example-dummy" />

    <configurations>
        <conf name="base" description="Jars required at both compile and runtime" />
        <conf name="taskdefs" description="Jars required for Ant Taskdefs" />
        <conf name="compile" description="Jars required at compile / package time" />
        <conf name="deploy" description="Jars required at deploy time" />
        <conf name="test" description="Jars required at test time" />
    </configurations>

    <dependencies>
        <dependency org="oracle/soa/bpel" name="orabpel" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/soa/bpel" name="orabpel-validator" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/soa/bpel" name="orabpel-common" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/soa/bpel" name="orabpel-thirdparty" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/soa/fabric" name="fabric-runtime" rev="11.1.1" conf="compile,taskdefs->default" />
        <dependency org="oracle/soa/mgmt" name="soa-infra-mgmt" rev="11.1.1" conf="compile,taskdefs->default" />
        <dependency org="oracle/soa/fabric" name="soa-infra-tools" rev="11.1.1" conf="compile,taskdefs->default" />
        <dependency org="oracle/soa/fabric" name="testfwk-xbeans" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/fabriccommon" name="fabric-common" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/xdk" name="xmlparserv2" rev="11.1.0" conf="compile->default" />
        <dependency org="commons-logging" name="commons-logging" rev="1.0.4" conf="compile->default" />
        <dependency org="commons-digester" name="commons-digester" rev="1.7" conf="compile->default" />
        <dependency org="commons-beanutils" name="commons-beanutils" rev="1.6" conf="compile->default" />
        <dependency org="commons-collections" name="commons-collections" rev="3.2.1" conf="compile->default" />
        <!--dependency org="commons-cli" name="commons-cli" rev="1.1" conf="compile,deploy->default" /-->
        <dependency org="oracle/commonj-sdo" name="commonj-sdo" rev="2.1.0" conf="compile->default" /> <!-- This is a hack as the Oracle JDeveloper one is different from the one in M2 repositories -->
        <dependency org="oracle/logging-utils" name="oracle.logging-utils" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/dms" name="dms" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/webservices" name="orawsdl" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/mds" name="mdsrt" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/jmx" name="jmxframework" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/adf/share" name="adf-share-base" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/adf/share" name="adf-logging-handler" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/odl" name="ojdl" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/http-client" name="oracle-httpclient" rev="11.1.1" conf="compile,deploy->default" />
        <dependency org="oracle/wsm/common" name="wsm-policy-core" rev="11.1.1" conf="compile->default" />
        <dependency org="oracle/classloader" name="oracle-classloader" rev="11.1.1" conf="compile->default" />
        <dependency org="com/bea/core" name="com-bea-core-apache-commons-lang" rev="2.1.0" conf="compile->default" />
        <dependency org="com/bea/core" name="com-bea-core-xml-xmlbeans" rev="2.2.0.0" conf="compile->default" />
    </dependencies>
</ivy-module>

NOTE: I haven’t managed to get Ivy to load configurations for the taskdefs yet (see below). I’ll update this post when I get it to work

NOTE: Only the configurations for compile/package and deploy are included in this. It’s pretty easy to add the declarations for the other paths you need to set. I’ll repost this once it’s complete.

The final step was to add Ivy support to the out-of-the-box ant files.  To do this we;

  1. Added the ivy.jar to our ant/lib directory
  2. Added the Ivy namespace to the ant file: 
  3. <project xmlns:ivy="antlib:org.apache.ivy.ant"
             name="ant-scac"
             default="scac">

  4. Removed the elements setting all classpaths (e.g. scac.tasks.class.path in ant-scac-compile.xml)
  5. Added a new path declaration just for the taskdefs. E.g.: 
  6. <!-- Set the Path we need for the Ant Taskdefs -->
        <property name="oracle.ant.taskdef.path" refid="oracle.ant.taskdef.path"/>
        <path id="oracle.ant.taskdef.path">
            <fileset dir="${applications.home}/lib">
                <include name="fabric-runtime.jar"/>
                <include name="soa-infra-mgmt.jar"/>
                <include name="soa-infra-tools.jar"/>
            </fileset>
        </path>

  7. Updated the taskdef declaration elements to use this new path. E.g:  <taskdef name="scac" classname="oracle.soa.scac.scac" classpath="${oracle.ant.taskdef.path}" />
  8. Added a new “init” task which sets the path we removed earlier (e.g. scac.tasks.class.path in ant-scac-compile.xml):
  9. <target name="init" description="Sets up the compilation classpath">
            <ivy:resolve />
            <ivy:cachepath conf="compile" pathid="scac.tasks.class.path" />
            <property name="scac.tasks.class.path" refid="scac.tasks.class.path"/>
        </target>

  10. Updated the targets to have a dependency on “init”. E.g.:
  11. <target name="scac" description="Compile and validate a composite" depends="init">
            <scac input="${scac.input}"
                  outXml="${scac.output}"
                  error="${scac.error}"
                  appHome="${compositeDir}"
                  failonerror="true"
                  displayLevel="${scac.displayLevel}">
            </scac>
        </target>

And that’s it. Unfortunately there’s not a zip file you can download with a few bat/sh files to run, but maybe I’ll get there one day. In the meantime, this should help you to get where we have.

3 comments:

Yoav Landman said...

Nice article!
Regarding Artifactory's Ivy integration - since 2.1.2 ( the most recent version, as of time of writing) you can perform xpath queries on ivy module files. So, for example, you can find all modules that use a specific dependency.
Artifactory also supports importing remote repository definitions - you may find the Oracle remote repository definition, which is provided in the default import list, useful for retrieving Oracle-specific dependencies.

Andrew Law said...

Hi Yoav,

Thanks for the comment. I wasn't aware of specific Ivy integration in Artifactory. I'll check it out.

I'll also take a look at the Oracle Remote Repo Definition. Could you send me a like about using this?

Cheers, Andrew

Yoav Landman said...

Sure -
In Artifactory go to 'Admin:Repositories', then in the 'Remote Repositories' section click 'Import'.
Click 'Load' with the default JFrog public repo URL, and you will get via REST a list of exposed public remote repo configurations, which Oracle is one of (it's on the second page) with pre-configured include/exclude patterns.
It seems the Oracle remote repo is not directly browsable, however, artifacts can be accessed by their direct link, e.g.: http://download.oracle.com/maven/com/sleepycat/je/3.3.75/je-3.3.75.pom