Thursday 6 December 2012

Generate Tradingpartners for Oracle B2B 11g with Ant

Introduction

At my previous customer, a Dutch energy infrastructure managing company, I worked on an implementation of AS2 (http://en.wikipedia.org/wiki/AS2) message exchanging with Oracle B2B (part of SOASuite 11g) The company needed to exchange information about energy delivery with other companies that supply and transport energy.

Problem

Since there are many companies in the enegery market that exchange messages with eachother, in our case, we needed to enter about 80 tradingpartners (TP's), that were very similar in message-exchange capabilities. Instead of entering those 80 TP’s in Oracle B2B by hand, which is a lot of error-prone work, I decided to see if it was possible to automate the process, by generating an export file that would serve as input property file for B2B. Hence, as a start I looked at the B2B selfservice scripts. With those (ANT) scripts you can generate an export file based on a set of definition input xml files. Afterwards this export file can be imported into B2B. This last step can be done manually, but you could even import (deploy) the generated export file and even deploy the agreements automatically.

Generating the export file from an addressing properties input Excel sheet 

In our case we had two roles: shippers and suppliers. There are a few differences between the two tradingpartner roles, but all the shippers have the same capabilities as well as all the suppliers.
Hence, shippers and suppliers send and receive about the same set of messages.
Also, all the TP’s are identified in the same way, using a code. Furthermore, all the addressing properties were delivered in an Excel sheet. So I could generate a property file naming all the TP’s with all their properties and their roles. Based on the TP’s role I determined if the TP should be enabled or not: only suppliers and shippers had to be imported, the rest of the TP's I disabled in the property file.

Lets take a look in how this was done.

Initializing

For the scripting I have some base targets and a base property file.
The property file:

dev.mode=false
#dev.mode=true
demo.mode=false
#demo.mode=true

#Generic Environment variables
wl_home=${wn.bea.home}/wlserver_10.3
ant-contrib.jar=${wn.bea.home}/modules/net.sf.antcontrib_1.1.0.0_1-0b2/lib/ant-contrib.jar

# temp
tmp.output.dir=c:/temp
log.dir=${basedir}/logs
host.tpl=${basedir}/templates/Host_tpl.xml
doctypes=${basedir}/templates/DocTypes.xml
supplier.tpl=${basedir}/templates/Supplier_tpl.xml
shipper.tpl=${basedir}/templates/Shipper_tpl.xml
b2bselfservice.tpl.dir=${basedir}/b2bselfservice
b2bselfservice.exp.file=${basedir}/zip/B2BExport.zip


junit.output.dir=../../

jndi.props.tpl=${basedir}/jndi.properties.template
jndi.props=${oracle.home}/bin/jndi.properties

Then I have a library.xml with a LogMessage target:
<?xml version="1.0" encoding="windows-1252" ?>
<project name="library">
      <macrodef name="logMessage">
            <attribute name="message" default=""/>
            <attribute name="level" default="debug"/>
            <sequential>
                  <echo message="@{message}" level="@{level}"></echo>
                  <!-- <echo file="${log.file}" append="true"
                        message="@{message}${line.separator}" level="@{level}"></echo> -->
                  <echo file="${log.file}" append="true"
                        message="@{message}${line.separator}"></echo>
            </sequential>
      </macrodef>
</project>

Now and the start of my build.xml:

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- B2B Utilities
@author Martien van den Akker, Darwin-IT Professionals
@version 0.1, 2012-11-22
-->
<project name="B2BScripting" basedir=".">
    <property environment="env"/>
    <property name="deploy.basedir" value="${basedir}"/>
    <!-- Default environment property dir -->
    <property file="${basedir}/build.properties"/>
    <property file="${env.prop.dir}/tradingPartner.properties"/>
    <import file="${basedir}/library.xml"/>
    <taskdef resource="net/sf/antcontrib/antlib.xml">
        <classpath>
            <pathelement location="${ant-contrib.jar}"/>
        </classpath>
    </taskdef>
    <!-- Deploy Targets -->
    <!-- Clean Up logs -->
    <target name="clean">
        <echo>Delete log dir ${log.dir}</echo>
        <delete dir="${log.dir}"/>
    </target>
    <!-- initialize project -->
    <target name="init" depends="clean">
        <echo>Dev mode: ${dev.mode}</echo>
        <echo>Target environment: ${deployment.plan.environment}</echo>
        <echo message="Create log dir" level="debug"></echo>
        <mkdir dir="${log.dir}"/>
        <!-- Build time -->
        <tstamp>
            <format property="build.date" pattern="yyyy-MM-dd HH:mm:ss"/>
        </tstamp>
        <!-- Build number -->
        <condition property="build.number" value="${env.BUILD_NUMBER}">
            <isset property="env.BUILD_NUMBER"/>
        </condition>
        <buildnumber file="build.num"/>
        <property name="log.file"
                  value="${log.dir}/instance-${build.number}.log"/>
    </target>
    <!-- log env properties -->
    <target name="logEnvProps" depends="init">
        <echo>BEA Home: ${wn.bea.home}</echo>
        <echo>Oracle Home: ${oracle.home}</echo>
        <echo>Java Home: ${java.home}</echo>
        <echo>Log File: ${log.file}</echo>
    </target>

The templates

As a starting point for the scripts I used the documentation on the B2B Utilities, see: B2B Command-Line Tools. Also I found the blog of Anuj Dwivedi on this subject very usefull.

You can put all the definitions of DocumentTypes, Trading partners and agreements in one file. Or you can split them up in several files. Important is that the different files have to be ordered alphabetically, first the documenttypes, then the ones with the tradingpartners and then the agreements. I decided to have the documenttypes in one file named 1_docTypes.xml, the host trading partner named 2_tp_1234567000004.xml and then the different tradingpartners named 3_TP-<id>.xml. Where the <id> is expanded to the 18-digit code of the particular remote TP. And I put the agreements and the tradingpartner-definitions per tradingpartner in one file.

The documentypes

The documenttypes are defined as follows:

<?xml version="1.0" encoding="windows-1252" ?>
<SelfService xmlns="http://xmlns.oracle.com/integration/b2b/selfservice/profile">
  <DocumentProtocols>
    <DocumentProtocol name="Custom">
      <DocumentProtocolVersion name="MSGORG_V1.2">
        <!-- METER -->
        <DocumentType name="METERAcknowledgementDocType">
          <DocumentDefinition customFileType="true"
                              definitionFileName="../../../EnergyCieMDS/apps/MSGORG/xsd/v1.2/METERAcknowledgement_1p2.xsd"
                              name="METERAcknowledgement"
                              useDefaultDefinition="false">
            <ParameterValue name="IdentificationExpression"
                            value='/*[local-name()="METERAcknowledgementEnvelope"]'/>
          </DocumentDefinition>
        </DocumentType>
        <DocumentType name="METERNotificationDocType">
          <DocumentDefinition customFileType="true"
                              definitionFileName="../../../EnergyCieMDS/apps/MSGORG/xsd/v1.2/METERNotification_1p2.xsd"
                              name="METERNotification"
                              useDefaultDefinition="false">
            <ParameterValue name="IdentificationExpression"
                            value='/*[local-name()="METERNotificationEnvelope"]'/>
          </DocumentDefinition>
        </DocumentType>
      </DocumentProtocolVersion>
    </DocumentProtocol>
  </DocumentProtocols>
</SelfService>

You see that the main tag is "SelfService". This tag applicable for every selfservice-xml-file.  So all the definitions should be contained within the  "SelfService"  tag.
The xsd that this xml implements can be generated using the SelfService ant utilities that are delivered with SOASuite as follows:

set BEA_HOME=D:\Oracle\Middleware\SOASuite11g
set ORACLE_HOME=%BEA_HOME%\Oracle_SOA1
set ANT_HOME=%BEA_HOME%\modules\org.apache.ant_1.7.1
set PATH=%ANT_HOME%\bin;%PATH%
set JAVA_HOME=%BEA_HOME%\jdk160_24 

ant -f %ORACLE_HOME%\bin\ant-b2b-util.xml  b2bselfservicexsd 
This delivers an XSD that you can register in JDeveloper. Doing so, JDeveloper can help you edit the XML, since it knows what nodes and attributes come where.

The Host Tradingpartner

The host tradingpartner comes next in line, here's my sample template:
<?xml version="1.0" encoding="windows-1252" ?>
<SelfService xmlns="http://xmlns.oracle.com/integration/b2b/selfservice/profile">
  <TradingPartners>
    <TradingPartner hosted="true" name="${hostTp.name}">
      <!-- Host Trading Partner -->
      <Identification name="Name" value="${hostTp.name}"/>
      <Identification name="Generic" value="${hostTp.generic.name}"/>
      <Identification name="AS2 Identifier" value="${hostTp.AS2.id}"/>
      <DeliveryChannel ackMode="None" compressed="false" internal="true"
                       listening="false"
                       name="${hostTp.short}_AQ_IN_INT_Channel"
                       responseMode="None" retryCount="3" retryInterval="3">
        <ExchangeProtocolRef name="Generic-AQ"/>
        <TransportProtocolRef name="AQ">
          <ParameterValue name="queue_name" value="IP_IN_QUEUE"/>
          <ParameterValue name="datasource" value="jdbc/SOADataSource"/>
        </TransportProtocolRef>
      </DeliveryChannel>
      <SupportedDocumentDefinition docDefName="METERAcknowledgement"
                                   docProtocolName="Custom"
                                   docProtocolVersion="MSGORG_V1.2"
                                   docTypeName="METERAcknowledgementDocType"
                                   initiator="false"/>
      <SupportedDocumentDefinition docDefName="METERNotification"
                                   docProtocolName="Custom"
                                   docProtocolVersion="MSGORG_V1.2"
                                   docTypeName="METERNotificationDocType"
                                   initiator="true"/>
    </TradingPartner>
  </TradingPartners>
</SelfService>

So here you see that the name of the tradingpartner as well as the AS2 Identifier is filled with an ANT property that will come from  a property file that I will give later on.
Since in our case the Tradingpartners where identified by 18-digit-numbers, I added a "Generic" identifier for a more human-readable name. After the identifications come the Delivery Channels, and after that the SupportedDocumentDefinitions: the capabilities. The Internal Delivery Channel I used was the AQ, which I think is the preferred.

The Remote TradingPartner

<?xml version="1.0" encoding="windows-1252" ?>
<SelfService xmlns="http://xmlns.oracle.com/integration/b2b/selfservice/profile">
  <TradingPartners>
    <TradingPartner hosted="false" name="${tp.name}">
      <Identification name="AS2 Identifier" value="${tp.AS2.id}"/>
      <Identification name="Name" value="${tp.name}"/>
      <Identification name="Generic" value="${tp.generic.name}"/>
      <DeliveryChannel listening="false"
                       name="${tp.name}_AS2_OUT_EXT_SNGENC_Channel"
                       internal="false">
        <ExchangeProtocolRef name="AS2"/>
        <TransportProtocolRef name="HTTP">
          <ParameterValue name="url" value="${tp.AS2.host}"/>
          <ParameterValue value="true" name="use_proxy"/>
        </TransportProtocolRef>
        <DigitalSecurity ackSigned="true" messageEncrypted="true"
                         messageSigned="true" transportSecured="true">
          <DigitalSignature securitySpecificationName="SMIME-3_0-SHA-RSA"
                            certAlias="${hostTp.cert.alias}"/>
          <DigitalEncryption securitySpecificationName="SMIME-3_0-DES"
                             certAlias="${tp.cert.alias}"/>
          <!--     <TransportSecurity securitySpecificationName="SSL"
                             certAlias="${tp.cert.alias}"/> -->
        </DigitalSecurity>
      </DeliveryChannel>
      <SupportedDocumentDefinition docDefName="METERAcknowledgement"
                                   docProtocolName="Custom"
                                   docProtocolVersion="MSGORG_V1.2"
                                   docTypeName="METERAcknowledgementDocType"
                                   initiator="true"/>
      <SupportedDocumentDefinition docDefName="METERNotification"
                                   docProtocolName="Custom"
                                   docProtocolVersion="MSGORG_V1.2"
                                   docTypeName="METERNotificationDocType"
                                   initiator="false"/>
     
    </TradingPartner>
  </TradingPartners>
  <Agreements>
    <!--METER-->
    <Agreement agreementId="${tp.name}_Custom_MSGORG_V1.2_METERAcknowledgementDocType_METERAcknowledgement_Inbound"
               name="${tp.name}_Custom_MSGORG_V1.2_METERAcknowledgementDocType_METERAcknowledgement_Inbound"
               effectiveFromDate="${agreement.effectiveFromDate}" >
      <SupportedDocumentType docProtocolName="Custom"
                             docProtocolVersion="MSGORG_V1.2"
                             docTypeName="METERAcknowledgementDocType"
                             docDefName="METERAcknowledgement">
        <InitiatingParticipant name="${tp.name}">
          <Identifications>
            <IdentificationRef name="Name" value="${tp.name}"/>
            <IdentificationRef name="AS2 Identifier" value="${tp.AS2.id}"/>
          </Identifications>
          <DeliveryChannels>
            <DeliveryChannelRef name="${tp.name}_AS2_OUT_EXT_SNGENC_Channel"/>
          </DeliveryChannels>
        </InitiatingParticipant>
        <RespondingParticipant name="${hostTp.name}">
          <Identifications>
            <IdentificationRef name="Name" value="${hostTp.name}"/>
            <IdentificationRef name="AS2 Identifier" value="${hostTp.name}"/>
          </Identifications>
          <DeliveryChannels>
            <DeliveryChannelRef name="${hostTp.short}_AQ_IN_INT_Channel"/>
          </DeliveryChannels>
        </RespondingParticipant>
      </SupportedDocumentType>
    </Agreement>
    <Agreement agreementId="${tp.name}_Custom_MSGORG_V1.2_METERNotificationDocType_METERNotification_Outbound"
               name="${tp.name}_Custom_MSGORG_V1.2_METERNotificationDocType_METERNotification_Outbound"
               effectiveFromDate="${agreement.effectiveFromDate}" >
      <SupportedDocumentType docProtocolName="Custom"
                             docProtocolVersion="MSGORG_V1.2"
                             docTypeName="METERNotificationDocType"
                             docDefName="METERNotification">
        <InitiatingParticipant name="${hostTp.name}">
          <Identifications>
            <IdentificationRef name="Name" value="${hostTp.name}"/>
            <IdentificationRef name="AS2 Identifier" value="${hostTp.name}"/>
          </Identifications>
          <DeliveryChannels/>
        </InitiatingParticipant>
        <RespondingParticipant name="${tp.name}">
          <Identifications>
            <IdentificationRef name="Name" value="${tp.name}"/>
            <IdentificationRef name="AS2 Identifier" value="${tp.AS2.id}"/>
          </Identifications>
          <DeliveryChannels>
            <DeliveryChannelRef name="${tp.name}_AS2_OUT_EXT_SNGENC_Channel"/>
          </DeliveryChannels>
        </RespondingParticipant>
      </SupportedDocumentType>
    </Agreement>

  </Agreements>
</SelfService>
The Remote trading partner is basically the same, except that I can have more of these and in stead of the AQ Delivery Channel, I have a AS2 Delivery channel. And of course the capabilities, the SupportedDocumentDefinitions are complementary to those of the Host. That means that where the host is Initiator, the Remote TP is not and vice versa.

Within each TP I also have agreements for each SupportedDocumentDefinition stating the correct Identifications and delivery channels.

And that's about it for the templates.

 The Script

Propertyfile

First the property file of the tradingpartners. I named the file tradingpartner.properties:
tp.list=1234567000005,1234567000006,1234567000004 
hostTp.name=1234567000004
hostTp.short=NMI
hostTp.generic.name=NetManager Inc.
hostTp.AS2.id=1234567000004
hostTp.cert.alias=1234567000004netManager
agreement.effectiveFromDate=2012-11-27T00:00:00+01:00
#agreement.effectiveToDate=
# Trading Partner Props
#tp.enabled=true
#tp.name=
#tp.role=
#tp.generic.name=
#tp.AS2.id=
#tp.AS2.host=
#tp.cert.alias=
#tp.SSLcert.alias=
#
# 1234567000005 - SupplierCie
1234567000005.enabled=true
1234567000005.name=1234567000005
1234567000005.role=Supplier
1234567000005.generic.name=Supplier Cie
1234567000005.AS2.id=1234567000005
1234567000005.AS2.host=https://b2b-prd.SupplierCie.nl/as2-endpoint
1234567000005.cert.alias=1234567000005SupplierCie
#1234567000005.SSLcert.alias=
#
# 1234567000006 - ShipperCie
1234567000006.enabled=true
1234567000006.name=1234567000006
1234567000006.role=PV/shipper
1234567000006.generic.name=Shipper Cie
1234567000006.AS2.id=1234567000006
1234567000006.AS2.host=https://b2b-prd.ShipperCie.nl/as2-endpoint
1234567000006.cert.alias=1234567000006ShipperCie
#1234567000006.SSLcert.alias=
#
# 1234567000004 - NetManager Inc.
1234567000004.enabled=false
1234567000004.name=1234567000004
1234567000004.role=NNO
1234567000004.generic.name=NetManager Inc.
1234567000004.AS2.id=1234567000004
1234567000004.AS2.host=https://b2b.netmanager.com/b2b/httpreceiver
1234567000004.cert.alias=1234567000004NMI.cer
#1234567000004.SSLcert.alias=

Create All TP Definitions

The folllowing target is the main target for generating the trading partner definitions.
It starts with copying the doctype template to the folder where the selfservice files are gathered. Then the selfservice file for the host TP is generated based on the template explained above. At the end for each tradingpartner named in the tp.list in the tradingpartner property file the corresponding selfservice file is generated. The last step is calling the target that calls the selfservice api to generate the B2B export zip-file.
 <!-- Create All TP Definitions -->
    <target name="createAllTPDefinitions" depends="logEnvProps">
        <logMessage message="createAllTPDefinitions" level="info"/>
        <logMessage message="basedir ${basedir}" level="info"/>
        <logMessage message="date = ${build.date}" level="info"/>
        <logMessage message="build = ${build.number}" level="info"/>
        <logMessage message="environment = ${deployment.plan.environment}"
                    level="info"/>
        <logMessage message="List of TradingPartners = ${tp.list}"
                    level="info"/>
        <delete dir="${b2bselfservice.tpl.dir}"/>
        <delete file="${b2bselfservice.exp.file}"/>
        <mkdir dir="${b2bselfservice.tpl.dir}"/>
        <logMessage message="======> Copy ${doctypes}" level="info"/>
        <copy file="${doctypes}"
              tofile="${b2bselfservice.tpl.dir}/1_docTypes.xml"
              overwrite="true">
            <filterchain>
                <expandproperties/>
            </filterchain>
        </copy>
        <copy file="${host.tpl}"
              tofile="${b2bselfservice.tpl.dir}/2_tp_${hostTp.name}.xml"
              overwrite="true">
            <filterchain>
                <expandproperties/>
            </filterchain>
        </copy>
        <foreach list="${tp.list}" param="tradingPartner"
                 target="createTPDefinitions" inheritall="true"
                 inheritrefs="false"/>
        <!-- Create B2B Export -->
        <antcall target="b2bCreateExport"/>
    </target>

Create the Remote Trading Partner definitions

Below the target for the generation of the Remote TP definitions.
It does a copy of the properties form the selected trading partner to corresponding generic  properties. These are used in the expansion of the properties in the tradingpartner template on copying the file to the selfservice file for the selected tradingpartner. Since there are two tradingpartner-types in our case (Shippers and Suppliers) you'll find an if on the tradingpartner role. Based on the role the specific template is being copied to the remote tradingpartner's selfservice file.

<!-- Create TradingPartner Definitions -->
    <target name="createTPDefinitions">
        <logMessage message="createTPDefinitions" level="info"/>
        <propertycopy name="tp.enabled" from="${tradingPartner}.enabled"/>
        <echo message="${tradingPartner} enabled:${tp.enabled}"></echo>
        <if>
            <equals arg1="${tp.enabled}" arg2="true"/>
            <then>
                <logMessage message="${line.separator}====>Create config for Tradingpartner ${tradingPartner}"
                            level="info"/>
                <propertycopy name="tp.name" from="${tradingPartner}.name"/>
                <propertycopy name="tp.role" from="${tradingPartner}.role"/>
                <propertycopy name="tp.generic.name"
                              from="${tradingPartner}.generic.name"/>
                <propertycopy name="tp.AS2.id" from="${tradingPartner}.AS2.id"/>
                <propertycopy name="tp.AS2.host"
                              from="${tradingPartner}.AS2.host"/>
                <propertycopy name="tp.cert.alias"
                              from="${tradingPartner}.cert.alias"/>
                <logMessage message="======> Name: ${tp.name}" level="info"/>
                <logMessage message="======> Role: ${tp.role}" level="info"/>
                <logMessage message="======> Generic Name: ${tp.generic.name}"
                            level="info"/>
                <logMessage message="======> AS2.id: ${tp.AS2.id}"
                            level="info"/>
                <logMessage message="======> AS2.host: ${tp.AS2.host}"
                            level="info"/>
                <logMessage message="======> cert.alias: ${tp.cert.alias}"
                            level="info"/>
                <if>
                    <equals arg1="${tp.role}" arg2="Supplier"/>
                    <then>
                        <logMessage message="======> Copy ${supplier.tpl} for ${tp.name}"
                                    level="info"/>
                        <copy file="${supplier.tpl}"
                              tofile="${b2bselfservice.tpl.dir}/3_tp_${tp.name}.xml"
                              overwrite="true">
                            <filterchain>
                                <expandproperties/>
                            </filterchain>
                        </copy>
                    </then>
                </if>
                <if>
                    <equals arg1="${tp.role}" arg2="PV/shipper"/>
                    <then>
                        <logMessage message="======> Copy ${shipper.tpl} for ${tp.name}"
                                    level="info"/>
                        <copy file="${shipper.tpl}"
                              tofile="${b2bselfservice.tpl.dir}/3_tp_${tp.name}.xml"
                              overwrite="true">
                            <filterchain>
                                <expandproperties/>
                            </filterchain>
                        </copy>
                    </then>
                </if>
            </then>
            <else>
                <logMessage message="${line.separator}====>Skip Tradingpartner ${tradingPartner}"
                            level="info"/>
            </else>
        </if>
    </target>

Create B2B Export

The above targets generate the document types, host-tradingpartner and remote tradingpartner definitions in files in a specific folder, denoted by the ${b2bselfservice.tpl.dir} property.
Based on the generated files in that folder the b2bexport zip can be generated, using the following target:
<target name="b2bCreateExport">
        <logMessage message="${line.separator}====>Create B2B Export"
                    level="info"/>
        <if>
            <equals arg1="${demo.mode}" arg2="false"/>
            <then>
                <ant antfile="${oracle.home}/bin/ant-b2b-util.xml"
                     inheritall="false" target="b2bselfservice">
                    <property name="input" value="${b2bselfservice.tpl.dir}"/>
                    <property name="output" value="${b2bselfservice.exp.file}"/>
                </ant>
            </then>
        </if>
    </target>

Conclusion

The export-file that is generated in the last target can then be imported to the target environment.
This import can be done with the import/export tooling under the administration tab within the b2bconsole. I found that somehow it only works with internet explorer. Using Firefox or Chrome I get an error-message stating that the Zip is not valid.
It is also possible to import the file using scripting like I explained above. But since this blog is already becoming a heavy one, I keep that for a following blog. Keeps me writing. And actually with the explanation above you should be able to figure that out...

Tuesday 20 November 2012

Target B2BUI

When you already did something with B2B11g, then you probably already know how to connect to the B2B UI, and have found out that it's not available by default.

By default the B2B UI is not targetted to a managed server. It can be done very easy in the weblogic console (http://adminserver-host:port/console). Go to Deployments and look for b2bui, when found click on it.

Then check the boxes of the  b2bui components and click on "Change Targets" (this button will activate after checking the boxes).
Then in the next screen click yes:
 And after a while the changes are applied:
After this the b2b console can be addressed via: http://managedserver-host:port/b2bconsole.

Monday 12 November 2012

Export Exception in B2B11g

Recently I renamed some docTypes in B2B11g because of a typo. Last week I wanted to export the repository to put it into subversion and to be able to propagate the implementation to the test environment.

But when exporting I ran into the following exception:
MDS-00001: exception in Metadata Services layer MDS-00511: failure to create document /soa/b2b/Custom/DocVersion_V1.2/MyAcknowledgementDocType /MyAcknowledgement /MyAcknowledgement.xsd 
\DocVersion_V1.2\MyAcknowledgementDocType \MyAcknowledgement \MyAcknowledgement_1p2.xsd 
C:\Windows\TEMP\Export817535\soa\b2b\Custom\DocVersion_V1.2\MyAcknowledgementDocType \MyAcknowledgement \MyAcknowledgement_1p2.xsd (The system cannot find the path specified)

What I actually did in the renaming was to create a new doctype with docdefinition, along the incorrect ones, with copy/pasting the oldnames and changing the typo (...Acknowlegdement... into ...Acknowledgement...). But apparently I got an extra trailingspace in my new name.  It appeared in two docTypes.

I removed the deployments of the particular agreements, removed the document registrations at the Trading Partners (both host and remote) and then removed the DocType under the Administration/Documents menu.

After that I could create the documentTypes again (now payed extra attention to possible trailing spaces), recreated the document registration at the partners and the agreements. After that I could export the repository succesfully again.

Bottom line: you apparently can have trailing spaces in the names, but this will lead into export errors...

B2B11g with Apache 2.0 as forward proxy

In the past I wrote an article about high availability architecture for Oracle Integration B2B10g.
Currently I'm working on an AS2 implementation with Oracle B2B11g. B2B11g is now part of SOASuite11g. Although it can be seen as a gateway product, much like OSB, since it is part of SOASuite I would not recommend to place B2B in de DMZ (Demilitarized Zone).

So what you need is a proxy and reverse proxy in the DMZ to forward messages from B2B to your trading partner (forward proxy) and route messages from your trading partner to B2B (reverse proxy).

The idea in our setup was actually to use Microsoft IIS for this. The reverse proxy was succesfull implementated, where it also functions as a SSL-decoder. But the IIS-expert had difficulties to turn the forward-proxy functioning on. Now that should not be to hard, but it need to have proxy, and proxy-connect (for proxying SSL-requests) installed. Apparently those are seperate installs for IIS. You may conclude: me know nothing about IIS. Also, the IIS-expert was not around very much and we needed a proxy in place pretty much immediatly.

So I installed Apache for that. I installed a Apache 2.0.x. Not the latest  release, I know, because I hoped to be able to re-use my settings from my earlier implementation. However, those turned out to be from 1.3 (forgot about that).

Anyway I had to change my settings a little.

First you need to turn some modules (mod_proxy, mod_proxy_http and mod_proxy_connect) on in the http.conf

#2012-10-31, M. van den Akker, Darwin-IT Professionals
# mod_proxy en mod_proxy_http needed for proxy
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so

The proxy-connect module is needed to be able to proxy for SSL connections.
Since I like to seperate my customsettings from the other ones I created a separate config file for the proxy-settings. This I include at the end of the http.conf:

#2012-10-31, M. van den Akker, Darwin-IT Professionals
#Include proxy config
Include conf/proxy.conf

Then of course the proxysettings in the proxy.conf file:
#2012-10-31, M. van den Akker, Darwin-IT Professionals

Listen 1234
NameVirtualHost proxy-web-b2b:1234
ProxyRequests On
ProxyVia On
AllowCONNECT 443 8443 
<Proxy *>
  #Order deny,allow
  #Deny from all
  Allow from all
</Proxy> 
<VirtualHost proxy-web-b2b:1234>
  ServerAdmin webmaster@proxy-web-b2b
  ErrorLog logs/error_proxyfwd1234.log
  CustomLog logs/access_proxyfwd1234.log common
</VirtualHost>
The AllowCONNECT setting denotes the ports that are allowed for SSL-Connect forwards.

Since we use this temporarly only for forward proxying, I don't have the Apache 2.0 settings for reverse proxying at hand. Since B2B11g is the initiator of the https-connection it needs to have the SSL certificates of remote tradingpartners, besides the AS2 encryption/signing certificates in the keystore. So as is done in our case it does not make much sense to have the reverse-proxy (IIS in our implementation) do the un-SSL-ing (decoding). Then you'll need to maintain those certificates in two keystores.


For the forward proxy the weblogic server of B2B needs to be started with the following settings:
  • -Dhttp.proxySet=true
  • -Dhttp.proxyHost=proxy-web-b2b
  • -Dhttp.proxyPort=1234
In the B2B configuration on the delivery channels the checkbox "Use Proxy" needs to be checked.

That's about it.

Wednesday 31 October 2012

JDeveloper 11g: Wrong Component Types in project

Did you encounter it as well? You're working wih a SOA-project within JDeveloper 11g and suddenly you see that there are componenttypes in your project (within the application navigator) that do not belong to your project. Componenttypes of  mostly BPEL components that are from other projects, even in other applications (.jws).

I encountered this in both Jdeveloper 11g PS4 (11.1.1.5 ) and PS5 (11.1.1.6). But probably because I work with those now more intensively. Probably this occurs in earlier versions too.

Now, I found that it is probably a bug in JDeveloper because sometimes I work within a component, say a BPEL process, and go in the application navigator to another project to look something up. To see how I did it in that project or how a particular xsd looks like. Then I work further, but the Application Navigator is open in that other project. JDeveloper apparently looses track of the actual source folder and writes the changes in my component in to that other project. I assume it is when you add a partnerlink, eg. an adapter, where the component type has to be changed with references.

Until now I just deleted those "wandering" component types. And found no problems or inconsistencies in the original/correct componenttypes within their projects.
But I hope this bug is solved.
Maybe someone that reads this and can file a bug within Oracle could do so? I would be greatfull. For the rest be carefull...

Wednesday 12 September 2012

Forget about WSIF: welcome Spring

A few years ago I was very proud that I managed to call java from BPEL through WSIF. I think that the method pretty much should work in 11g. But my method back than used the deprecated tool Schema Compile. So I would need to figure out how to do that with JAXB or something. But now in the later patchsets of Weblogic/SOASuite there is a far more nicer method: Spring. Now, my experience with Spring starts since yesterday... Spring is a method to embed simple java objects (Plain Old Java Objects: POJO's) into an application server context. In SOASuite it is implemented in a real cool way, so that you can embed them in a real declarative way. If you google your way around you can find several examples on Spring and SOASuite. Documentation you can find here. And Edwin Biemond created an article about it. But unfortunately, it did not become clear to me how I had to implement my setup.

In my previous article I explained how to call Oracle Reports from java. I want to call that from BPEL or Mediator. Earlier I thought about the Socket adapter or the HTTP service-reference. But those options failed because the examples I found expect XML as an input (REST), where I needed to call an URL with parameters. Or they prevented me to call a url on port 80 (on which our reportservers listen...).
In that article you read that I had a service, and the input is a bean and the response is a bean. The reason of that was that I needed to pass multiple attributes as parameter and receive multiple attributes as a response. In reallife you might have a service with a bean structure as a request and response that reflect a complete hierarchical XML structure. The examples I found just talked about classes with elementary datatypes as  parameters and results. So how to set up your spring context to be able to do your call? At first I thought I needed just one context for the service. But it turns out that you have to create a context for every bean that plays a part in the interface (basically your service and every bean in the call parameter and response class structure).

To be able to expose the java classes to a WSDL (that is generated for you when you wire the service to BPEL) you have to generate Interfaces on your classes. So building further on my previous story, that would be on the classes ReportRequest, ReportResponse and CallReportsService.
This can be generated for you as follows. Open the ReportRequest class and right click in the source, choose Refactor and then Extract Interface:
Then you can select all the methods you want to expose:


In my previous article I put a method in my request bean to construct the Reports Server url (getReportsURL()). This method I don't want to have exposed in my WSDL. So I did not select that in the list. You can do the same for the other two classes.

Now it's time to declare the Spring-bean-structure. Open the composite editor and right-click:
When you choose Insert SpringContext, you might get a "Spring Not Available" error:
Go to Help->Check for Updates and search for the Spring & Weblogic SCA Addon:
Add it and restart JDeveloper.

Now you can add a SpringContext to your composite:
 Edit the Spring Context (CallReportsServer.xml). Then in the component palette you can look for the Weblogic SCA tab, and drag a service into the composite:

Name it CallReportsService, and as a target the CallReportsServiceBean. As a type you can browse to your classes, choose here the interface ICallReportsService.
Now you can add a bean to the context. The tag for this can be found on the "Spring 2.5 Core" page. But you can also type it in of course:
 Then you have to define it. Convenient is the Inspector palette:

 The Id of the bean should be the same as the target of the service: CallReportsServiceBean.
The class can be browsed the same way as the type in the Service. Choose here the implementation class "CallReportsService".

Now we can do the same for the other two beans. For the two input/output beans (ReportRequest and ReportResponse) I choose the bean implementation itself as the type. In my first setup I encountered that at Runtime SOASuite tries to call the public no-parameter-constructor of the type. Since an interface does not have a constructor, it failed. So I replaced that with the implementation bean.

If you have the three beans you can tie them together:
This gets you the following context definitions.
CallReportsServer.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="CallReportsService" target="CallReportsServiceBean"
               type="nl.darwinit.soa.callreportsservice.ICallReportsService"/>
  <bean id="CallReportsServiceBean"
        class="nl.darwinit.soa.callreportsservice.CallReportsService"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.ReportRequest"
                 name="ReportRequest.ReportRequest"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.ReportResponse"
                 name="ReportResponse.ReportResponse"/>
</beans>

ReportRequest.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="ReportRequest" target="ReportRequestBean"
               type="nl.darwinit.soa.callreportsservice.ReportRequest"/>
  <bean id="ReportRequestBean"
        class="nl.darwinit.soa.callreportsservice.ReportRequest"/>
</beans>

ReportResponse.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="ReportResponse" target="ReportResponseBean"
               type="nl.darwinit.soa.callreportsservice.ReportResponse"/>
  <bean id="ReportResponseBean"
        class="nl.darwinit.soa.callreportsservice.ReportResponse"/>
</beans>

Well, actually that is about it. Now lets create a BPEL process.


I changed the xsd to:
<?xml version="1.0" encoding="UTF-8"?> 
<schema attributeFormDefault="unqualified"
 elementFormDefault="qualified"
 targetNamespace="http://xmlns.darwin-it.nl/CallReportsService/CallReportsService/CallReportsService"
 xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
 <element name="process">
  <complexType>
   <sequence>
    <xsd:element name="aanvraagID" type="xsd:string" minOccurs="0"/>
    <xsd:element name="destination" type="xsd:string" minOccurs="0"/>
    <xsd:element name="httpTimeOut" type="xsd:int" minOccurs="0"/>
    <xsd:element name="jobName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="previewJN" type="xsd:string" minOccurs="0"/>
    <xsd:element name="reportName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="reportsBaseUrl" type="xsd:string" minOccurs="0"/>
    <xsd:element name="userId" type="xsd:string" minOccurs="0"/>
   </sequence>
  </complexType>
 </element>
 <element name="processResponse">
  <complexType>
   <sequence>
    <xsd:element name="errorCode" type="xsd:string" minOccurs="0"/>
    <xsd:element name="errorMessage" type="xsd:string" minOccurs="0"/>
    <xsd:element name="jobId" type="xsd:string" minOccurs="0"/>
    <xsd:element name="jobStatus" type="xsd:string" minOccurs="0"/>
    <xsd:element name="jobStatusCode" type="xsd:string" minOccurs="0"/>
    <xsd:element name="responseXml" type="xsd:string" minOccurs="0"/>
   </sequence>
  </complexType>
 </element>
</schema>


Now we can tie the CallReportServer component to the BPEL Component:

Trying this will get a message that asks you to compile the classes, click yes:
 
 After the compilation, the wiring has to be redone, which will result in the message that the wsdl is created:
Now you can implement the BPEL Process.
Since I choose the ReportRequest implementation bean as a type in the ReportRequest Spring Context, it turns out that it generated an element for the reportURL getter. You could remove that from the wsdl, but since it is not exposed as a service, you can also leave it that way. Just don't map it to any input.

Now, you would think you're done. But there is one little caveat in the deployment of the spring contexts. We tied the beans neatly together. But the Spring Context of the CallReportServer.xml expects in the references Interfaces as a type. So change those to the interfaces of the beans:

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="CallReportsService" target="CallReportsServiceBean"
               type="nl.darwinit.soa.callreportsservice.ICallReportsService"/>
  <bean id="CallReportsServiceBean"
        class="nl.darwinit.soa.callreportsservice.CallReportsService"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.IReportRequest"
                 name="ReportRequest.ReportRequest"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.IReportResponse"
                 name="ReportResponse.ReportResponse"/>
</beans>
(nl.darwinit.soa.callreportsservice.ReportRequest changed to nl.darwinit.soa.callreportsservice.IReportRequest, same for IReportResponse)

Now you can deploy and test.

As you can see, it's a very declarative way to create a webservice on a java class.

I wrapped up my complete project here.





Call Oracle Reports from the Middle Tier And Parse/XPath query the response

In my current project we're replacing Oracle Workflow with SOASuite/BPEL 11g. One of the problems turn out to be Oracle Reports in batch. There is a procedure in the database that queries for all requests for which an Oracle Report has to be run. For each of them it calls another procedure that constructs the URL to the Reports server and invokes it. The response is searched for a status, for an OK status.

Since potentially there can be hundreds or thousands of letters to be printed for requests, this procedure can run for a very long hours. Up to maybe three hours or so. From Oracle Workflow this is no problem at all, since it runs in the database in a background job itself. Normally you have one or more background engines running in dbms_jobs. The OWF processes can be rebuild in BPEL as easily. The batch procedure is then called through the database adapter. But since it can run for hours (potentially) the database-session needs to be open all the time. The inherent synchronous database adapter needs a time out of hours. And even then you might run into a time-out. So for BPEL this is not a good architecture.

There are several options to solve it. We could make the pl/sql service asynchronous, by creating a request and reply queue in Advanced Queueing. Then subscribe and register a pl/sql function that dequeues the message, calls the batch procedure and queues the reply message to the reply queue, using a correlation id. BPEL enqueues on the request queue a message to request for a batch run. And the wait for the correlated reply message. A good explanation on how to set that up is here on Ask Tom.

But I thought it would be nicer to call the Reports right from the middle tier. Then I would need to query the Requests for which to print in one database call, and for each one call the Oracle Report one by one. (Did you know that BPEL 2.0 had a very nice For-loop construction?)

So I needed to be able to call Reports from Java.

My Basic example I found here, is:
import java.net.*;
import java.io.*;

public class URLConnectionReader {
    public static void main(String[] args) throws Exception {
        URL yahoo = new URL("http://www.yahoo.com/");
        URLConnection yc = yahoo.openConnection();
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                yc.getInputStream()));
        String inputLine;

        while ((inputLine = in.readLine()) != null)
            System.out.println(inputLine);
        in.close();
    }
}

My first technical working example was:
   CallReportsService callReportsService = new CallReportsService();
        StringBuffer reportsUrl = new StringBuffer("http://reportsserver.darwin-it.local/reports/rwservlet?outputimageformat=gif&statusformat=xml&server=rep_dev_darwin&");
        reportsUrl.append("report=");
        String reportName = "DWN1234R";
        reportsUrl.append(reportName);
        reportsUrl.append("&P_PREVIEW=");
        String preview = "J";
        reportsUrl.append(preview);
        reportsUrl.append("&P_AVR_ID=");
        String aanvraagID = "7249834";
        reportsUrl.append(aanvraagID);
        reportsUrl.append("&JOBNAME=");
       
       
        String jobName = "DWN1234R_PREVIEW";
        reportsUrl.append(jobName);
                                         
                
        URL reportSvrURL = new URL(reportsUrl.toString());
       
        URLConnection reportSvrConnection = reportSvrURL.openConnection();
        reportSvrConnection.setConnectTimeout(60000);
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                reportSvrConnection.getInputStream()));
        String inputLine;

        while ((inputLine = in.readLine()) != null)
            System.out.println(inputLine);
        in.close();
    }"

Now eventually I want to have this running in a Spring context (had to figure that out too). So I want a Request and a Response bean. Since the response of the Reports server is in XML format, I want to have a ResponseParser. And then of course the service itsself.

The main runReport method of the CallReportsService is as follows:
    /**
     * Run a Report on the Report Server
     * @param repReq
     * @return ReportResponse
     */
    public ReportResponse runReport(ReportRequest repReq) {
        StringBuffer strBuf = new StringBuffer();
        ReportRequest repRequest = repReq;//(ReportRequest)iRepReq;
        
        ReportResponse repResp = null;
        URL reportSvrURL;
        try {
            reportSvrURL = new URL(repRequest.getReportsUrl());

            URLConnection reportSvrConnection = reportSvrURL.openConnection();
            reportSvrConnection.setConnectTimeout(repRequest.getHttpTimeOut());
            BufferedReader in =
                new BufferedReader(new InputStreamReader(reportSvrConnection.getInputStream()));
            String inputLine;
            do {
                inputLine = in.readLine();
                if (inputLine != null) {
                    strBuf.append(inputLine);
                    strBuf.append('\n');
                }
            } while (inputLine != null);
            in.close();
            ReportResponseParser repParser =
                new ReportResponseParser(strBuf.toString());
            repResp = repParser.getReportResponse();
        } catch (MalformedURLException e) {
            if (repResp == null)
                repResp = new ReportResponse();
            repResp.setErrorCode(EC_MALFORMEDURL);
            repResp.setErrorMessage("MalformedURLException: " +
                                    e.getLocalizedMessage());
        } catch (IOException e) {
            if (repResp == null)
                repResp = new ReportResponse();
            repResp.setErrorCode(EC_IO);
            repResp.setErrorMessage("IOException: " + e.getLocalizedMessage());
        }
        return repResp;
    }

It uses a request bean as an input. The request bean contains the method to build up a request url:
    /**
     * Build a URL-String from the paratamers in the request
     */
    public String getReportsUrl() {
        StringBuffer reportsUrl = new StringBuffer(getReportsBaseUrl());
        reportsUrl.append("report=");
        reportsUrl.append(getReportName());
        reportsUrl.append("&userid=");
        reportsUrl.append(getUserId());
        reportsUrl.append("&destination=");
        reportsUrl.append(getDestination());
        reportsUrl.append("&P_PREVIEW=");
        reportsUrl.append(getPreviewJN());
        reportsUrl.append("&P_AVR_ID=");
        reportsUrl.append(getAanvraagID());
        reportsUrl.append("&JOBNAME=");
        reportsUrl.append(getJobName());
        return reportsUrl.toString();
    }
The attribute "reportsBaseUrl" of the bean should contain the base URL to the reports server: "http://reportsserver.darwin-it.local/reports/rwservlet?outputimageformat=gif&statusformat=xml&server=rep_dev_darwin&"

To parse the XML content I created a few helper methods in the ReportResponseParser class. The first one is the actual parser:
        /**
     * Parse the Response XML String
     */
    private void parseRespXmlStr() {
        try {
            InputSource inputSource = getRespXMLAsInputSource();
            if (inputSource != null) {
                DocumentBuilder docBuilder;
                docBuilder = newDocBuilder();
                Document doc;
                doc = docBuilder.parse(inputSource);
                setDoc(doc);
            }
        } catch (ParserConfigurationException e) {
            setErrorMsg("ParserConfigurationException: " +
                        e.getLocalizedMessage());
            setErrorCode(EC_PARSING);
        } catch (SAXException e) {
            setErrorMsg("SAXException: " + e.getLocalizedMessage());
            setErrorCode(EC_PARSING);
        } catch (IOException e) {
            setErrorMsg("IOException: " + e.getLocalizedMessage());
            setErrorCode(EC_PARSING);
        }
    }
It needs an InputStream. So the String object that is the result from the ReportsServer is to be read into an InputSource:
   /**
     * Create an InputSource/InputStream for the response XML String
     * @return
     */
    private InputSource getRespXMLAsInputSource() {
        String respXMLStr = getResponseXMLStr();
        InputSource inputSource = null;
        if (respXMLStr != null) {
            inputSource = new InputSource();
            inputSource.setCharacterStream(new StringReader(respXMLStr));
        }
        return inputSource;
    }

Possibly, since I catch all the lines in the response into a StringBuffer, I could do it from there as well. But these methods I had "on the shelf". Then I need a DocumentBuilder that does the parsing.
    /**
     * Create a new DocumentBuilder
     * @return
     * @throws ParserConfigurationException
     */
    private DocumentBuilder newDocBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory domFactory =
            DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        DocumentBuilder docBuilder = domFactory.newDocumentBuilder();
        return docBuilder;
    }

To evaluate an xpath expression on the XML Document I need the next method:
    /**
     * Evaluate xpath expression
     *
     * @param xpathExpr the xpath expression
     * @param returnType the return type that is expected.
     * http://www.ibm.com/developerworks/library/x-javaxpathapi.html:
     * XPathConstants.NODESET => node-set maps to an org.w3c.dom.NodeList
     * XPathConstants.BOOLEAN => boolean maps to a java.lang.Boolean
     * XPathConstants.NUMBER => number maps to a java.lang.Double
     * XPathConstants.STRING => string maps to a java.lang.String
     * XPathConstants.NODE
     *
     * @throws XPathExpressionException
     */
    public Object evaluate(String xpathExpr,
                           QName returnType) throws XPathExpressionException {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        /*XMLNSResolver nsRes = getNsRes();
           if (nsRes != null) {
               xpath.setNamespaceContext(nsRes);
           }*/
        XPathExpression expr = xpath.compile(xpathExpr);
        Document doc = getDoc();
        Object resultObj = expr.evaluate(doc, returnType);
        return resultObj;
    }

Now, the result is an Object. And we expect a NodeList:
    /**
     * Select Nodes using Xpath
     *
     * @param xpath
     * @return NodeList
     * @throws XPathExpressionException
     */
    public NodeList selectNodes(String xpath) throws XPathExpressionException {
        NodeList nl = (NodeList)evaluate(xpath, XPathConstants.NODESET);
        return nl;
    }

The queries I want to perform on the response are basically single strings. So the next method just gets the simple text content of the queried node:
    /**
     * Get the text content of a node.
     * @param xpath
     * @return
     */
    public String getXpathTextContent(String xpath) {
        String result = null;
        try {
            NodeList nl = selectNodes(xpath);
            if (nl.getLength() > 0) {
                result = nl.item(0).getNodeValue();
            }
        } catch (XPathExpressionException e) {
            setErrorMsg("XPathExpressionException: " +
                        e.getLocalizedMessage());
            errorCode = EC_XPATH;
            setErrorCode(errorCode);
        }
        return result;
    }

Then you can querie all the relevant elements as follows:
    /**
     * Get the Error Code from the Reponse
     * @return
     */
    public String getRespErrorCode() {
        String errorCode = getXpathTextContent("//serverQueues/error/@code");
        return errorCode;
    }

    /**
     * Get the Error Message from the Reponse
     * @return
     */
    public String getRespErrorMsg() {
        String errorMsg = getXpathTextContent("//serverQueues/error/@message");
        return errorMsg;
    }
    /**
     * Get the job Id from the Reponse
     * @return
     */
    public String getRespJobId() {
        String errorMsg = getXpathTextContent("//serverQueues/job/@id");
        return errorMsg;
    }
    /**
     * Get the job status from the Reponse
     * @return
     */
    public String getRespJobStatus() {
        String errorMsg = getXpathTextContent("//serverQueues/job/status");
        return errorMsg;
    }  
    /**
     * Get the job status Codefrom the Reponse
     * @return
     */
    public String getRespJobStatusCode() {
        String errorMsg = getXpathTextContent("//serverQueues/job/status/@code");
        return errorMsg;
    }
    /**
     * Parse the responseXML into  a ReportResponse bean
     * @return
     */
    ReportResponse getReportResponse() {
        ReportResponse repResp = new ReportResponse();
        repResp.setErrorCode(this.getRespErrorCode());
        repResp.setErrorMessage(this.getRespErrorMsg());
        repResp.setResponseXml(this.getResponseXMLStr());
        repResp.setJobId(this.getRespJobId());
        repResp.setJobStatus(this.getRespJobStatus());
        repResp.setJobStatusCode(this.getRespJobStatusCode());
        return repResp;
    }

In the end it is pretty simple. Most of the code is to be able to call the service with a nice Request bean and  get a response bean back. You could of course get the fields you want using string-manipulation methods. But I found the xml-parsing a more neat method. If you need more info from the response it is easy to query them. Tip: use the xpath-search (from the Search menu) tool from JDeveloper to test your xpath queries.
A tipical Reports response xml is:

<?xml version = '1.0' encoding = 'UTF-8' standalone = 'yes'?>
<serverQueues>
   <job id="5159" queueType="past">
      <name>DWN1234R_PREVIEW</name>
      <type>report</type>
      <status code="4">Rapport  is voltooid.</status>
      <owner>RWUser</owner>
      <server>rep_dev_darwin</server>
      <destination>
         <desType>unknown</desType>
         <desFormat>dflt</desFormat>
      </destination>
      <timingInfo>
         <queued>11-sep-2012 12:45:14</queued>
         <started>11-sep-2012 12:45:14</started>
         <finished>11-sep-2012 12:45:14</finished>
      </timingInfo>
   </job>
</serverQueues>

I put my classes in a SOA project. Because the next step is to create a spring context for them. You can download the project here.

Wednesday 29 August 2012

Implement message transport security in B2B11g

One of my current customers is in the transition of migrating from sending/receiving EDI messages to XML over AS2. The choice of B2B-product is Oracle SoaSuite-Integration B2B11g.

Nice, because in the past I was intensively involved in implementing ebMS with B2B10g. I think the first implementation on Oracle Integration B2B 10g with ebMS in the Netherlands. Unfortunately I was not able to get message encryption and signing working back then. Mainly because of lack of time and lack of knowledge on different certificate formats. Now I had the change to re-try quite the same thing in B2B 11g.

AS2 and ebMS are regarding implementation basically the same: both are transport protocols over HTTP(s). I had to implement SSL and Message Encription and Signing.

My colleague had already found a good starting point: the blog of Anuj Dwivedi on Enabling SSL on Oracle B2B 11g. It's a nice step by step "how-to" to implement transport security.

Unfortunately I missed 2 aspects in the story: how to import a private certificate in a ".p12" file and how to convert a certificate in DER format to PEM. My customer has a certificate in pkcs12 (a .p12 file) format and that one needs to be imported. Then the public certicate is not in PEM format, and that needs to be to get it imported in the JKS keystore using the keytool as described in the blog.

Import private certificate in the keystore

This how-to I found here. This is done by first creating an empty key store:
keytool -genkey -alias darwinKey -keystore ~/Keystores/b2bKeyStore.jks -
keypass welcome1 -storepass welcome1
This creates a JKS-keystore with a key with alias darwinKey. It asks several questions like:
What is your first and last name?
[Unknown]: Martien van den Akker
What is the name of your organizational unit?
[Unknown]: Professionals
What is the name of your organization?
[Unknown]: Darwin-IT
What is the name of your City or Locality?
[Unknown]: Amersfoort
What is the name of your State or Province?
[Unknown]: Utrecht
What is the two-letter country code for this unit?
[Unknown]: NL
Is CN=Martien van den Akker, OU=Professionals, O=Darwin-IT, L=Amersfoort,
ST=Utrecht, C=NL correct?
[no]: yes
Since we want an empty keystore in which we import a private key, we need to delete it:
keytool -delete -alias darwinKey -keystore ~/Keystores/b2bKeyStore.jks -
storepass welcome1
Now the keystore is empty we can import the pkcs12 certificate:
keytool -v -importkeystore -srckeystore ~/Certificaten/
darwinCustomerCertificate.p12 -srcstoretype PKCS12 -destkeystore ~/
Keystores/b2bKeyStore.jks -deststoretype JKS -storepass welcome1
Output:
Enter source keystore password:
Entry for alias darwincustomer successfully imported.
Import command completed: 1 entries successfully imported, 0 entries
failed or cancelled
[Storing /home/oracle/Keystores/b2bKeyStore.jks]
Now I used as a keystore password "welcome1" in the examples above. Later I found that to enable B2B to read the certificate and actually use it, the password of the keystore had to be the same as the passphrase of the private key. So it is best when using the examples above for your actual implementation to use that password right away. But luckily the password can also be changed:
keytool -storepasswd -keystore ~/Keystores/b2bKeyStore.jks
Output:
Enter keystore password:
New keystore password:
Re-enter new keystore password:

Convert DER certificate to PEM

When you get a public certificate, you probably get it in a kind of binary format. You can also export it from your private key in your keystore with the keytool utility:
keytool -exportcert -alias darwinCustomerCert -file ~/Certificaten/
darwinCustomerPublicCert.cer -keystore ~/Keystores/b2bKeyStore.jks -storepass
welcome1
Output:
Certificate stored in file </home/oracle/Certificaten/darwinCustomerPublicCert.cer> 
The exported certificate is stored in DER-format. To create a PEM file out of it, OpenSSL can be used. The command for this, I found here and here.
openssl x509 -inform der -in darwinCustomerPublicCert.cer -outform pem -out
darwinCustomerPublicCert.pem
This will deliver a readable certificate that is importable in the keystore.

Monday 20 August 2012

Change Subversion Password in Jdeveloper 11g

At one of my customers I use JDeveloper 11g also as a subversion client. It is actually my first experience of the SVN implementation of JDeveloper, because I'm not able to install a tool like TortoiseSVN on this computer.

I'm pretty positive on the svn-client in Jdev. But unfortunately I haven't found any means to change the password in the UI. At this customer my windows-password has to be changed monthly, and the svn-password depends on it.

Jdeveloper stores the subversion connect info in the file

$APPLICATION_DATA\Roaming\JDeveloper\system11.1.1.6.38.61.92\o.jdeveloper.subversion\repositories.xml
(where "system11.1.1.6.38.61.92" depends on the Jdeveloper build).
The file looks like:
<?xml version = '1.0' encoding = 'UTF-8'?>
<svn-repositories xmlns="http://xmlns.oracle.com/jdeveloper/1013/svn/repositories">
   <svn-repository>
      <url>https://svn.customer.nl/svn/odc/odc-cmr/APPLICATION/source/trunk</url>
      <alias>Application Trunk</alias>
      <user-name>makker</user-name>
      <password>PlainOrEncryptedPasword</password>
   </svn-repository>
</svn-repositories>
Remarkable is the version number in the namespace (1013), since it really comes from my Jdev11g install.

When you first create the connection and give in your username/password via the UI, the password will be stored in here encrypted. But you can also give in an unencrypted password. So I entered my new password in plain format. And it works.

I haven't found how I can encrypt the password, what would, of course, be better. If anyone has a tip, then I'm interested.

You can also rename or delete the file,  then JDeveloper will ask for the new server connection.

Friday 17 August 2012

Database 11g: change hostname

Sometimes I want to duplicate or reuse a Virtual Machine for another purpose. This week I had to start with a setup for a B2B communication for a client. So I pick one, duplicate them and change the particular hostname and corresponding network settings (/etc/hosts) to reflect the particular situation. So that I can communicate to the instance with its own host-alias.

It is not exactly necessary, but it is neat to have the hostname of the server changed to the particular situation. But if you have installed an Oracle 11g database, you'll encounter that you can't login at it anymore.
Some how the database checks the hostname and with the ip-address of the server its installed on.
I found the solution here in the doc, see paragraph 3.3.2.2. Network.

You'll have to make sure that the name of your host (see cat /etc/sysconfig/network) is in the /etc/hosts file with the ip address of the machine.
So my first network adapter is a host-only with a fixed address of 10.0.0.1. As such I installed the database. If I change my hostname to 'darwin-vce-soa', for instance then in the /etc/hosts there should be a line like:

10.0.0.1          darwin-vce-soa.darwin-it.local    darwin-vce-soa

Also make sure that you change the listner.ora and tnsnames.ora in $ORACLE_HOME/network/admin.

Oh, and if you are on a demo/development server, you may want to have password expiry turned off. See my blog-entry here.

Wednesday 15 August 2012

Change hostname of weblogic server

If you change the host name of the server that runs your weblogic server, then connecting to a page in the weblogic server (/console, /em, etc) may cause page load errors.
When installing the weblogic server, it registers the current hostname as the name of the server in the domain config. It will do a URL-rewrite to that hostname when connecting to a webapplications.

To change the weblogic-hostname, go to config folder of the domain home and edit the config.xml.
under //server/web-server/frontend-host you find the host that is used for the url rewrite. Edit it according to your hostname settings:
<?xml version='1.0' encoding='UTF-8'?>
<domain xmlns="http://xmlns.oracle.com/weblogic/domain" xmlns:sec="http://xmlns.oracle.com/weblogic/security" xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/security/xacml http://xmlns.oracle.com/weblogic/security/xacml/1.0/xacml.xsd http://xmlns.oracle.com/weblogic/security/providers/passwordvalidator http://xmlns.oracle.com/weblogic/security/providers/passwordvalidator/1.0/passwordvalidator.xsd http://xmlns.oracle.com/weblogic/domain http://xmlns.oracle.com/weblogic/1.0/domain.xsd http://xmlns.oracle.com/weblogic/security http://xmlns.oracle.com/weblogic/1.0/security.xsd http://xmlns.oracle.com/weblogic/security/wls http://xmlns.oracle.com/weblogic/security/wls/1.0/wls.xsd">
  <name>fmw_domain</name>
  <domain-version>10.3.5.0</domain-version>
...
  <server>
    <name>AdminServer</name>
 ...  
    <listen-port>7001</listen-port>
    <listen-port-enabled>true</listen-port-enabled>
    <web-server>
      <!--<frontend-host>old-host</frontend-host>-->
      <frontend-host>fmwhost-vm</frontend-host>

Host only network in VMware player on Window7 not working

Today I was to setup a B2B configuration. So I wanted to have two SOASuite11g VM's working together on my laptop. My colleague created a VM for this particular client in VMWare Player, so I wanted to use that one, in stead of one of my VirtualBox VM's.

One of the features I rely on in this setup is the host-only network. Since it is a B2B setup, I need the two VM's to communicate together, so they have to be able to ping each other over the same network. Although internally in the VM the particular network adapter will get an ip-address. But it was not "pingable" from the host.

I was about to uninstall VMWare Player and re-install it to get it defaulted again. But probably it would not make a difference in this. Luckily I found this terrific post that helped me out. Thanks Tino. And it gives some tips about how to do some VMnet changes on the commandline that cannot be done UI-wise in VMware Player.

It turns out that in Windows 7 the VMNet1 adapter on the host was set on manual setings having a an auto-ip address (eg. 169.254.x.x).

This can be solved by going to the "Network and Sharing Center" (the English name for the German "Netzwerk- und Freigabecenter"). Click on the "VMware Network Adapter VMnet1". And then:
  • Click on the button Properties
  • Disable "Internet Protocol Version 6 (TCP/IPv6)". 
  • Select "Internet Protocol Version 4 (TCP/IPv4)" and click on the button "Properties"
  • Set the radio button on "Obtain an IP address automatically"
Then it solved my problem. 

Friday 8 June 2012

Subversion Id in your SOASuite service

Wouldn't it be nice to see what particular version you have deployed on your SOASuite test or production environment. How often does it occur that you thought you solved a problem but it still does not work on test or production. And after doing loads of tests and investigation you encounter that not the version you expected was deployed?

This week I tried to add the svn Id keyword in a piece of comments in the WSDL of the service. But it turns out that at deployment the SOASuite filters out the comments. So that won't work.
But since I'm writing this blog you might expect I found a trick...

First add the svn keywords  to the files you want to have the properties in. In this particular case it would be the service wsdl. This can be done with Tortoise SVN by right clicking the file, and choose TortoiseSVN->Properties. Then clicking the new button you can choose for Keywords:


 After that you can check the SVN properties you like to use. For most cases ID gives you all the info you need.
 Clicking OK will get you back to the properties screen, where you'll see:

Mark that in the list of checkboxes the Id-keyword is all in capitals, while in the comma-seperated list of keywords in the properties pane, the Id-property is in the correct spelling (one capital I).
The trick is to add the property as a namespace declaration as:
  xmlns:svnid="$Id$"
  xmlns:version="1.1"


After a SVN-commit this will expand to:
  xmlns:svnid="$Id: ImportEquipment.wsdl 765 2012-06-08 13:07:10Z makker $"
  xmlns:version="1.1"
Like such:

The reason I added a "version" namespace as well is that your wsdl will not change every time the service changes. Often you change a bit of BPEL or XSL or something like that, while your interface, the wsdl, stays unchanged. To force SVN increase the revisionid in your wsdl, you can change the xmlns:version namespace.

Thursday 7 June 2012

Delete svn subfolders

Here and there migrate/upgrade projects for SoaSuite10g to SoaSuite11g are running. Most of the time the source is committed to Subversion. It turns out that JDeveloper makes a full copy of the project folder at upgrade.
This means that also the .svn folders are copied. And you probably don't want that. I would have the new project in a different branch in my subversion folder.

At a former customer there was a registry file that creates a menu item in the Windows explorer pop-up menu. This would hierarchically delete all .svn folders in the selected folder. But not allways you're able to install such a shell-command.

So I created a little ant script. Since we're working with JDeveloper11g, we have an Ant install. I also created a windows bat file to run the ant script with a folder that can be given as a command line parameter.

The build.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<project default="run">
  <target name="run">
    <tstamp/>
    <property environment="env"/>
    <property name="scan.folder" value="${env.SCAN_FOLDER}" />
    <!--<property file="./build.properties"/>-->
    <echo message="Delete all .svn from ${scan.folder}"></echo>
    <delete includeemptydirs="true"  verbose="true">
      <fileset dir="${scan.folder}" includes="**/.svn/**,**/.svn" defaultexcludes="false"/>
    </delete>
  </target>
</project>
The fileset in the delete can be found as an example in the doc of the Ant Delete Task. However, I found that the example did not work as such, for .svn folders with content. So I expanded the includes property to "**/.svn/**,**/.svn" to delete the content of .svn folders, before deleting them.
I set the verbose attribute, to see which folders/files are actually deleted.

The delete deleteSVNFolders.bat file is:
@echo off
cls
set ORACLE_HOME=d:\oracle\Product\JDeveloper11116\Middleware
set ANT_HOME=%ORACLE_HOME%\jdeveloper\ant
set PATH=%ANT_HOME%\bin;%PATH%
set JAVA_HOME=%ORACLE_HOME%\jdk160_24
set SCAN_FOLDER=%1
ant

You'll have have to change the ORACLE_HOME setting to point to your JDeveloper middleware home.
Of course this works on linux as well, but than you'll have to transform the bat file to a bash script. But that's no rocket science...

Monday 4 June 2012

Using MDS in SOASuite11g Projects

In a SOA application there will be many services that are dependent on each other. Naturally one would develop a service, deploy it to a server and in a next service refer to that deployed service. That means that in the new project a reference is created based on a concrete WSDL that resides on a remote server. Often the WSDL and related XSD’s are copied to the referencing project. This causes several copies of basically the same XSD and WSDL throughout the whole SOA application system. Maintaining these xsd’s is a tedious job. At compilation of a project the server delivering the deployed service should be up and running. And at the end the deployment of the whole application to a new server (System Test, Acceptance Test, Production) is constrained in the order of deploying the different components of the application.

The solution in SOASuite 11g is to use the Meta Data Store (MDS). There are a few blog-entries on how to relate to XSD’s/WSDL’s in the MDS on the server and how to synchronize the XSD’s and WSDL’s. For a good starting point the following two blog entries are helpful:
However, these blog entries don’t provide an overall description on how to setup your project/application for using the MDS. Also these entries don’t show how to relate the application to a shared local “MDS” folder elsewhere besides in the Jdeveloper install folder. In a regular project you would place the artifacts in a subversion (SVN) server. So the references to the XSD’s and WSDL’s would be to a folder structure in the SVN working copy and that won’t (should not) reside in the Jdeveloper install folder.
That leaves me to tie the pieces together. So I created a demo application to work-out the details and wrote a whitepaper about it. You can download the whitepaper from our site.

Wednesday 16 May 2012

Import Oracle Workflow in BPM Suite 11g

Cool! Since BPM 11g PS4FP you can import Oracle Workflow models in BPM 11g. From PS5 (11.1.1.6) it should be possible as well. See this whitepaper.
I'm going to try. 

Monday 14 May 2012

Using JDeveloper HTTP Analyser to intercept/forward requests


In my previous entry I showed how to create a SoapUI test case to do a WS-Adressing driven request-response interaction with an asynchronous BPEL Process. But how does this WS-Adressing request look like?
To get this above the water a tool like the HTTP Analyser of JDeveloper 11g may come in handy.

It is found in the tools option of JDeveloper:


This opens the HTTP Analyser normally at the bottom of the screen:

To use it for our purpose we have to create a HTTP-listener with a forwarding rule. This means that it just listens to request and forwards the request to a URL we provide.
To do this I added a new listener with a new port 8100:
Then click on the "Configure Rules" button, add a forward rule (click on the dropdown triangle of the Add button and choose Forward Rule):
In the reference URL part in the screen above you can put a URL that is used to test the URL Filter.
But after playing around I just left this URL Filter field.

In the Target URL I put the complete URL of the original endpoint of the WSDL from SoapUI, found in the Test Request step in SoapUI. But this one I've replaced by adding a new endpoint:


The added endpoint will have the original URL but with the hostname:port replaced to localhost:8100 (where the HTTP Analyser is listening):

Thus:
http://localhost:8100/soa-infra/services/default/M10HelloWorld/helloworld_client_ep
Then you can run the HTTP Analyser:



I've copied and pasted the message from the screendump into a new XML Document in JDeveloper and reformated it:
 <?xml version="1.0" encoding="windows-1252" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:hel="http://xmlns.oracle.com/M10_Demo/M10HelloWorld/HelloWorld">
   <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <wsa:Action soapenv:mustUnderstand="1">process</wsa:Action>
      <wsa:ReplyTo soapenv:mustUnderstand="1">
         <wsa:Address>http://hostname.darwin-it.nl:8989/HelloWorldCallbackBinding/processResponse</wsa:Address>
      </wsa:ReplyTo>
      <wsa:MessageID soapenv:mustUnderstand="1">uuid:36f10438-0270-4318-a5a5-fd2ad44d6136</wsa:MessageID>
   </soapenv:Header>
   <soapenv:Body>
      <hel:process>
         <hel:input>Astrid</hel:input>
      </hel:process>
   </soapenv:Body>
</soapenv:Envelope>

Here you can see the properties set in SoapUI (see previous blog in the WSA-header:
  • Action - mustUnderstand="1"
  • Action (defaulted): process
  • ReplyTo - Address: http://hostname.darwin-it.nl:8989/HelloWorldCallbackBinding/processResponse
  • MessageID (generated): uuid:36f10438-0270-4318-a5a5-fd2ad44d6136
 Now with this configuration each request to localhost:8100 is forwarded to this process. If you need to support multiple processes different rules should be added or in stead of a forward rule a substitute rule should be applied.

A substitute rule will look like:
 With this each request to localhost:8100 is forwarded to hostname.darwin-it.nl:8001.