Thursday, 12 December 2013

Yet another one on 'Oracle Type inheritance and advanced type casting'

Triggered by a comment on an older article I wrote a little article yesterday on determining the instance of an object. As far as known by me at least and seemingly by Google there is no java instanceOf counterpart in Oracle Pl/Sql. In SQL Where clauses you can use the 'is of' expression. But that is not available in pl/sql. I tried some suggestions with embedded sql but I couldn't get it to work. The suggestion from a forum I found yesterday was to use function overloading. I played around a little with it and it seems I stumbled on a fairly simple method. Let's say I've a type like the following:
CREATE OR REPLACE TYPE "DWN_PERSON" AS OBJECT 
( 
name varchar2(40)
, member function instance_of return varchar2
) not final;
/
CREATE OR REPLACE TYPE BODY "DWN_PERSON" AS
  member function instance_of return varchar2 AS
  BEGIN
    return 'DWN_PERSON';
  END instance_of;
END;
/
I just created a simple Instance_of method that does nothing but returning the name of the object. It was my first step in making things spiffier later on. Keeping in mind that it probably should become an overloading function with a parameter. Then I created a Natural Person type:
  CREATE OR REPLACE TYPE "DWN_NATURAL_PERSON" under DWN_PERSON 
( surname varchar2(100)
, overriding member  function instance_Of return varchar2 
) ;
/
CREATE OR REPLACE TYPE BODY "DWN_NATURAL_PERSON" AS
  overriding member  function instance_Of return varchar2 AS
  BEGIN
    RETURN 'DWN_NATURAL_PERSON';
  END instance_Of;
END;
/
And because I was going so well, I created a Not Natural Person:
CREATE OR REPLACE TYPE "DWN_NOT_NATURAL_PERSON" under DWN_PERSON 
( companyname varchar2(100)
, overriding member  function instance_Of return varchar2 
) ;
/
CREATE OR REPLACE TYPE BODY "DWN_NOT_NATURAL_PERSON" AS
  overriding member  function instance_Of return varchar2 AS
  BEGIN
    RETURN 'DWN_NOT_NATURAL_PERSON';
  END instance_Of;
END;
/
Then I created the following little script:
declare
l_person1 dwn_person;
l_person2 dwn_person;
l_person3 dwn_person;

function create_person(p_name varchar2, p_surname varchar2, p_comany_name varchar2) return dwn_person
as
l_person dwn_person;
begin
  if p_surname is not null then
  l_person := dwn_natural_person(p_name, p_surname);
  elsif p_comany_name is not null then
    l_person := dwn_not_natural_person(p_name, p_comany_name);
  else
  l_person := dwn_person(p_name);
  end if;
 return l_person;
end;

begin
  l_person1 := create_person('Flip', 'Fluitketel', null);
  dbms_output.put_line('l_person1 is a '||l_person1.instance_of);
  l_person2 := create_person('Hatseflats', null, null);
  dbms_output.put_line('l_person2 is a '||l_person2.instance_of);
  l_person3 := create_person('Hatseflats', null, 'Hatseflats Inc.');
  dbms_output.put_line('l_person3 is a '||l_person3.instance_of);
end;
All three persons are declared as a 'DWN_PERSON'. So when I call the instance_of method of the particular object I expected that the method of the particular declared type is called. But, a little to my surprise, it turns out that Oracle uses the method of the Instantiated type:
l_person1 is a DWN_NATURAL_PERSON
l_person2 is a DWN_PERSON
l_person3 is a DWN_NOT_NATURAL_PERSON
Simple, but apparently very effective.

Wednesday, 11 December 2013

Another one on "Oracle Type inheritance and advanced type casting"

Already four and a half year ago I wrote an article on Oracle Type inheritance and type casting.  See here. But although Object types are supported by Oracle since Oracle 8, it seems to me that there is still little familiarity on the subject.
I'm not really a Pl/Sql programmer on daily basis anymore, the language is still close to me. And working with object types is a favorite subject to me ever since I discovered the endless use in Oracle 8i. Know Object types and you can do everything in Oracle.

Today I got a comment on the article, asking on if there is a solution on "downcasting" types. The subject in the mentioned article was that from an API (in the example from an EBusiness Suite12  API) you get an object type to which you want to add functionality without re-implementing/changing the delivered object type. The only thing to do it is to sub-type the delivered type. But how to come from an instance of a supertype to the use it as the subtype. Well, the answer is two fold:
  1. If the instance of the super type is actually instantiated as the supertype then you actually can't. As explained in the mentioned article, the only way is to instantiate the subtype with the attributes of the supertype. For what I think I found a smart generic solution.
  2. If you get an instance of the supertype, that was actually instantiated as the subtype then you can copy it again to a variable based on the subtypehttp://blog.darwin-it.nl/ and your good to go.
Lets elaborate on the second option. I'll do a dry swim, so the code presented is not tested in a database, so might not be free from syntax errors.

In Java there is a  statement called "InstanceOf". In Oracle SQL you have the "Is Of"construct. I found a forum post with the question about an "InstanceOf" analogy in Pl/Sql. As in the forum is stated the "Is Of" clause is in fact a possible where clause condition in a query. See also the documentation.

However in the post I saw a smart comment: create an overloaded procedure:
create procedure p_x(p_obj super_type);
do supertype code here

create procedure p_x(p_obj sub_type);
do subtype code here
Now let's say we have a type named 't_car' and a subtype say 't_opel_zafira under t_car':
create type t_car as object
(license_plate varchar2(10) )
/

create type t_opel_zafira under t_car 
(model varchar2(10))
/
Then you could create an overloaded function like
create function instance_of(p_obj t_car ) returns varchar2
as
begin
  return 't_car';
end;

create function instance_of(p_obj t_opel_zafira ) returns varchar2
as
begin
  return 't_opel_zafira';
end;
Then you could indeed do something like:
declare
  l_car t_car;
  l_opel_zafira t_opel_zafira;
begin
  l_car := instance_opel_zafira_as_car(...); -- This functions returns a value of type t_car but is in fact an Opel Zafira
  if instance_of(l_car) = 't_opel_zafira' then
    dbms_output.put_line('Enjoy your Opel Zafira');
  end if;
end;
I should try it, but you should even be able to add a such an instance_of member function to each object type. Going to try that tomorrow.

Wednesday, 20 November 2013

Ease up your SoaSuite deployment using global Tokens in composites

Yesterday a project-colleague pointed me to a cool new feature in SOASuite Patch set 6 (11.1.1.7).
Everyone who has done deployments of SOASuite throughout different stages within a Dev., Test, Acc. & Prod. lifecycle knows the throubles that end-pointreferences causes. One improvement in SOASuite 11g was the introduction of configuration plans. However those are not only environment specific, but also composite related. So for every composite you need one for every stage in the lifecycle.
In my release/deployment framework I solved this to have just a default generated configuration plan per composite, with development values. That one I 'subversioned' along with the composite.
Then in the (ant based) release proces I adapted the configuration plan for each composite, replacing each server reference with an ant-property. Then during the deploy I have conf plan copied to one that is specific for the target environment, replacing the properties based on a environment properties file.
Now in Patch Set 6, Oracle took that a level further. Now you can place those properties yourself in the composite on the references. There you replace the specific endpoints with those properties. In the EnterpriseManager you can bulk import the property values for the specific development-cycle stage using a "mdm-url-resolver.xml" xml file. But you can also enter or edit specific values one by one in the Enterprise Manager. For Ant users, the tokens have the same markup as Ant-properties.
This removes the need of configuration plans. Also it might be easier to clone for example a production environment to do for example a deployment test of  a new release.
Read all about it in the Oracle® Fusion Middleware Administrator's Guide for Oracle SOA Suite and Oracle Business Process Management Suite.

BTW.: this feature is mentioned to me by Tony van Esch, who wrote a little more elaborate blog on this himself.

Friday, 8 November 2013

Integrate BI-Publisher with OEM12c

Regularly I create VM's for courses as a 'Virtual Course Environment'. Earlier I did an install of Oracle Enterprise Manager 12c. But now there was a request for an integrated BI Publisher install.

Having an OEM12c install, the integration with BI Publisher with OEM12c is pretty straightforward. The install guide for this integration can be found here. This guide is part of the OEM12c 12.1.0.1 version, and for that version Oracle Business Intelligence Enterprise Edition 11g version 11.1.1.5. is required.

My OEM12c install is of version 12.1.0.2.0, so one patch set newer. Apparently the current OEM12c release is 12.1.0.3 (as of november '13).

The 11.1.1.6 release of BI-Publisher can be downloaded here. I initially installed the current 11.1.1.7 release that I downloaded here. However, when I ran the configureBIP script, from {OEM12c Home}/oms/bin, I got the following error:
[oracle@darlin-vce-db bin]$ ./configureBIP
Error: This script requires that BI EE Version "11.1.1.6.0" be installed, but "11.1.1.7.0" has been installed
So it's important to note that OEM12c install is of version 12.1.0.2.0 expects Oracle Business Intelligence Enterprise Edition 11g version 11.1.1.6.

By the way, the BI Publisher integration guide of OEM12c release 12.1.0.2.0  is found here. And 'surprisingly' it indeed states that it expects BI-Publisher 11.1.1.6. So, I could have known...

Wednesday, 10 July 2013

Current date and time in XLST under OSB

For my current assignment I needed to build quite complex transformations in OSB. Mainly because of my experiences I choose to do that in XSLT.
Eclipse has a quite nice XQuery mapper, but it lacks an XSLT mapper.
Since JDeveloper has a nice XSL Mapper tool, I incorporated a JDeveloper project in my OEPE OSB project.

Soon I found that the xpath2.0 functions of JDeveloper/SOASuite don't work under OSB. And I could not find how to use the XQuery functions in my Xslt, that do the same.

But I found a nice trick, mainly based on this forum post and this one. I adapted those examples a little, because of the wrong date formats and to get them working in my case.

First declare the following namspaces.
   xmlns:date="http://www.oracle.com/XSL/Transform/java/java.util.Date"
   xmlns:sdf= "http://www.oracle.com/XSL/Transform/java/java.text.SimpleDateFormat"
When examining those, it appears that "http://www.oracle.com/XSL/Transform/java/" tells the xsl-processor where to find java classes. And after that the path (package + class) to the actual java class can be appended. These prefixes should not get into the resulting xml, so you can add them to the exclude list:
     exclude-result-prefixes="... date sdf">
And then you can declare the next variables for the current date and the current time:
 
  <xsl:variable name="currentDate"><xsl:value-of select="sdf:format(sdf:new(&quot;yyyy-MM-dd&quot;),date:new())"></xsl:value-of></xsl:variable>
  <xsl:variable name="currentTime"><xsl:value-of select="sdf:format(sdf:new(&quot;hh:mm:ss&quot;),date:new())"></xsl:value-of></xsl:variable>
If you know java then you can figure out how that translates to a snippet of java code. And then you should be able to work out your way back. And doing so: you can program almost anything using java in XSLT...

Wednesday, 10 April 2013

Replacing JDK of JDeveloper 11.1.1.7 on 64 bit Oracle Linux 6

As mentioned yesterday, I installed Oracle SOA/BPM Suite 11g PS6 yesterday. It was on Oracle Linux 6 Update 3 (still have to update to Update 4).

Today I installed the accompanying JDeveloper 11.1.1.7 on the same machine. Everything went fine, except that apparently the installer failed to bring up a graphical UI.

Jdeveloper can then be started with


$JDEV_HOME/jdeveloper/jdev/bin/jdev
Where in my case in installed jdeveloper in:
$JDEV_HOME=/u01/app/oracle/jdeveloper/11.1.1.7

Doing so, to my surprice, I ran into the following exception:
[oracle@darlin-vce-db bin]$ ./jdev

Oracle JDeveloper 11g Release 1 (11.1.1.7.0)
Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 

java.lang.UnsatisfiedLinkError: /u01/app/oracle/jdeveloper/11.1.1.7/jdk160_24/jre/lib/i386/xawt/libmawt.so: libXext.so.6: cannot open shared object file: No such file or directory
 at java.lang.ClassLoader$NativeLibrary.load(Native Method)
 at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1806)
 at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1702)
 at java.lang.Runtime.load0(Runtime.java:770)
 at java.lang.System.load(System.java:1003)
 at java.lang.ClassLoader$NativeLibrary.load(Native Method)
 at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1806)
 at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1723)
 at java.lang.Runtime.loadLibrary0(Runtime.java:823)
 at java.lang.System.loadLibrary(System.java:1028)
 at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50)
 at java.security.AccessController.doPrivileged(Native Method)
 at sun.awt.NativeLibLoader.loadLibraries(NativeLibLoader.java:38)
 at sun.awt.DebugHelper.(DebugHelper.java:29)
 at java.awt.Component.(Component.java:567)
 at oracle.ide.IdeCore.startupImpl(IdeCore.java:1171)
 at oracle.ide.Ide.startup(Ide.java:710)
 at oracle.ideimpl.DefaultIdeStarter.startIde(DefaultIdeStarter.java:35)
 at oracle.ideimpl.Main.start(Main.java:184)
 at oracle.ideimpl.Main.main(Main.java:146)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at oracle.ide.boot.PCLMain.callMain(PCLMain.java:62)
 at oracle.ide.boot.PCLMain.main(PCLMain.java:54)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at oracle.classloader.util.MainClass.invoke(MainClass.java:128)
 at oracle.ide.boot.IdeLauncher.bootClassLoadersAndMain(IdeLauncher.java:189)
 at oracle.ide.boot.IdeLauncher.launchImpl(IdeLauncher.java:89)
 at oracle.ide.boot.IdeLauncher.launch(IdeLauncher.java:65)
 at oracle.ide.boot.IdeLauncher.main(IdeLauncher.java:54)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at oracle.ide.boot.Launcher.invokeMain(Launcher.java:714)
 at oracle.ide.boot.Launcher.launchImpl(Launcher.java:116)
 at oracle.ide.boot.Launcher.launch(Launcher.java:69)
 at oracle.ide.boot.Launcher.main(Launcher.java:58)
 

Nice!
Jdeveloper comes with it's own JVM. It's installed in $JDEV/jdk160_24. And is (as guessed) the following version:
[oracle@darlin-vce-db 11.1.1.7]$ cd jdk160_24/bin/
[oracle@darlin-vce-db bin]$ ./java -version
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b50)
Java HotSpot(TM) Server VM (build 19.1-b02, mixed mode)

Apparenlty it's 32 bit. On this machine I earlier installed the 64 Oracle/Sun Hotspot JDK in
[oracle@darlin-vce-db bin]$ set |grep JAVA_HOME
JAVA_HOME=/usr/java/jdk1.6.0_43
Checking it's version will get:
[oracle@darlin-vce-db bin]$ java -version
java version "1.6.0_43"
Java(TM) SE Runtime Environment (build 1.6.0_43-b01)
Java HotSpot(TM) 64-Bit Server VM (build 20.14-b01, mixed mode)
To solve the exception above, I changed the jdev.conf in $JDEV_HOME/jdeveloper/jdev/bin to use my own 64-bit JDK.
#
# Directive SetJavaHome is not required by default, except for the base
# install, since the launcher will determine the JAVA_HOME.  On Windows
# it looks in ..\..\jdk, on UNIX it first looks in ../../jdk. If no JDK
# is found there, it looks in the PATH.
#
#SetJavaHome /u01/app/oracle/jdeveloper/11.1.1.7/jdk160_24
SetJavaHome  /usr/java/jdk1.6.0_43/
So find the SetJavaHome directive at the top and replace it with the path to your own. Apparently you should also be able to use JRockit with JDeveloper. Would be a nice try. Although I have to try if, for instance, the compiler works, but with the above change, JDeveloper started up fine.

Tuesday, 9 April 2013

Starting SOA/BPM Suite with wlst via nodemanager


Today I installed the brand new SOA and BPM Suite 11g PS6. The last few months for me were dedicated in deepdiving WebLogic to deliver a Tuning and Troubleshooting course for two customers.

Although it was not the first time to for me to install the suite, and I already have several bash-based scripts to start the server using the weblogic shell scripts, I wanted to use my conceived knowledge to create a WLST based tool to start weblogic.

To create the tutorials for the course I already created a script to create and startup a new test domain with an adminserver and a managed server. And for the course I downsized the script to be able to start the domain. So I only had to adapt the script to be able to use it to start my SOABPM Domain.

Having it started using the nodemanager and also having a seperate AdminServer enables you to use the Admin Console to restart your SOAServer. So if you have enough memory it is the way to go. If you have not  enough memory choose the development topology at creation of the SOA/BPM domain. In that case the domain is created with only an admin server that contains both the Admin Console, EM and SOA/BPM.

Now the script is as follows:
#############################################################################
# Start SOABPM Domain 
#
# @author Martien van den Akker, Darwin-IT Professionals
# @version 0.1, 2013-04-09
#
#############################################################################
print 'Set Properties';
wlHome=os.getenv("WL_HOME")
nmHome=wlHome+'/common/nodemanager';
nmHost=os.getenv("NM_HOST");
nmPort=os.getenv("NM_PORT");
nmType=os.getenv("NM_TYPE");

domainsHome='/u01/app/work/domains';
SOABPMDomainName=os.getenv("SOABPM_DOMAIN_NAME");
SOABPMDomainHome=os.getenv("SOABPM_DOMAIN_HOME");
adminServerName=os.getenv("ADMIN_SERVER_NAME");
adminServerPort=os.getenv("ADMIN_SERVER_PORT");
SOAServerName=os.getenv("SOA_SERVER_NAME");

adminUser='weblogic';
adminPwd='welcome1';

print 'Start Nodemanager'
startNodeManager(verbose='true', NodeManagerHome=nmHome, ListenPort=nmPort, ListenAddress=nmHost);

print 'Connect to the Node Manager';
nmConnect(adminUser, adminPwd, nmHost, nmPort, SOABPMDomainName, SOABPMDomainHome, nmType);

print 'Start AdminServer';
nmStart(adminServerName);

print 'Connect to the AdminServer';
connect(adminUser, adminPwd);

print 'Start ManagedServer: '+SOAServerName;
start(SOAServerName);

If you set your wls settings as follows:
. $FMW_HOME/wlserver_10.3/server/bin/setWLSEnv.sh
export PATH=$WL_HOME/common/bin/:$WL_HOME/server/bin:$PATH

Then you can start the script as follows:
wlst.sh ./startSOABPMDomain.py

Provided that you have set your $FMW_HOME and $WL_HOME correctly (probably dependent on each other as in my case) and also the environment variables used in the script. And that you saved the script above as 'startSOABPMDomain.py'.

To provide for this and to enable the script to get the settings from the environment (and have the settings shared amongst shell and wlst scripts I have the following wls_env.sh script:
#!/bin/bash
echo Setting Weblogic Environment
export ORACLE_BASE=/u01/app/oracle
export FMW_HOME=/u01/app/oracle/Middleware/11.1.1
export DOMAINS_HOME=/u01/app/work/domains
export SOABPM_DOMAIN_NAME=SOABPMDomain
export SOABPM_DOMAIN_HOME=$DOMAINS_HOME/$SOABPM_DOMAIN_NAME
export NM_HOST=darlin-vce-db
export NM_PORT=5556
export NM_TYPE=plain
export OHS_HOME=/u01/app/work/instances/ohs1
export ADMIN_SERVER_NAME=AdminServer
export ADMIN_SERVER_PORT=7001
export SOA_SERVER_NAME=soa_server1

. $FMW_HOME/wlserver_10.3/server/bin/setWLSEnv.sh
export PATH=$WL_HOME/common/bin/:$WL_HOME/server/bin:$PATH

I prefer to get my settings from the environment. This is because I already have a bash script that sets the WLS and domain paths. This keeps things centralized. Note that I also have to set the paths to wlst.sh. If you prefer to load the properties in from a file in a more java way, then an example is found here. The stop script should look like something like:

#############################################################################
# Stop SOABPM Domain 
#
# @author Martien van den Akker, Darwin-IT Professionals
# @version 0.1, 2013-04-09
#
#############################################################################
print 'Set Properties';
wlHome=os.getenv("WL_HOME")
nmHome=wlHome+'/common/nodemanager';
nmHost=os.getenv("NM_HOST");
nmPort=os.getenv("NM_PORT");
nmType=os.getenv("NM_TYPE");

domainsHome='/u01/app/work/domains';
SOABPMDomainName=os.getenv("SOABPM_DOMAIN_NAME");
SOABPMDomainHome=os.getenv("SOABPM_DOMAIN_HOME");
adminServerName=os.getenv("ADMIN_SERVER_NAME");
adminServerPort=os.getenv("ADMIN_SERVER_PORT");
SOAServerName=os.getenv("SOA_SERVER_NAME");

adminUser='weblogic';
adminPwd='welcome1';

print 'Connect to the AdminServer';
connect(adminUser, adminPwd);

print 'Stop ManagedServer: '+SOAServerName;
shutdown(name=SOAServerName, entityType='Server', force='true');

print 'Stop AdminServer';
shutdown();

print 'Connect to the Node Manager';
nmConnect(adminUser, adminPwd, nmHost, nmPort, SOABPMDomainName, SOABPMDomainHome, nmType);

print 'Stop the NodeManager';
stopNodeManager();

A few things I encountered, that got me into writing this article are the following.

It is important that you have the listen-address of the machine, that the soa_server1 is added to, is the valid host-name of the server running your SOASuite. It cannot be 'localhost' which is defaulted by the SOA/BPM Domain configurator. The value 'localhost' simply does not work, although it is perfectly pingable. So update it according to your server (it should also point to the correct host-ip-address):


Also make sure you have Type set to 'Plain' and the property SecureListener=false in the $WL_HOME/common/nodemanager/nodemanager.properties accordingly. Here's my nodemanager.properties file as an example. The commented lines (except for the first one) denote the properties that are changed by me, the rest is default

#Fri Mar 15 14:28:18 CET 2013
DomainsFile=/u01/app/oracle/Middleware/11.1.1/wlserver_10.3/common/nodemanager/nodemanager.domains
LogLimit=0
PropertiesVersion=10.3
DomainsDirRemoteSharingEnabled=false
AuthenticationEnabled=true
NodeManagerHome=/u01/app/oracle/Middleware/11.1.1/wlserver_10.3/common/nodemanager
JavaHome=/usr/java/jdk1.6.0_43/jre
LogLevel=INFO
DomainsFileEnabled=true
StartScriptName=startWebLogic.sh
ListenAddress=darlin-vce-db
NativeVersionEnabled=true
ListenPort=5556
LogToStderr=true
#SecureListener=true
SecureListener=false
LogCount=1
DomainRegistrationEnabled=false
#StopScriptEnabled=false
StopScriptEnabled=true
QuitEnabled=true
LogAppend=true
StateCheckInterval=500
CrashRecoveryEnabled=false
#StartScriptEnabled=false
StartScriptEnabled=true
LogFile=/u01/app/oracle/Middleware/11.1.1/wlserver_10.3/common/nodemanager/nodemanager.log
LogFormatter=weblogic.nodemanager.server.LogFormatter
ListenBacklog=50


Then on the domain set the nodemanager's  username password correctly. I don't know the defaults, but they're probably not 'weblogic' and 'welcome1' as per my default values:

And then under Advanced:


Now ensure that you have enrolled your domain to the nodemanager.Check if it's listed in the $WL_HOME/common/nodemanager/nodemanager.domains file.

Some of my nodemanager-troubleshooting-knowledge is from this blog.

Hope this helps and at least save me some searching around the next time I have to setup a SOABPM-domain...

Oh, and of course you can split these files up to only start or stop the soaserver provided that the AdminServer and nodemanager are already started. And  to create separate start/stop for the BAM server, since I intentionally did not include that one in the scripts.

Monday, 1 April 2013

BPM Suite PS6 released

Just a moment ago I found out that BPM Suite PS6 is released for download: http://soacommunity.wordpress.com/2013/04/01/bpm-suite-ps6-11-1-1-7-available-for-download/

In a previous blog I also wrote about the OFMW Partner forum in Portugal, where I could play around with it.

This week and next week I have to teach a Tuning and Troubleshooting training on Weblogic 11g. But there after I certainly going to play with it. Hope to provide you some images then.

Friday, 29 March 2013

Transparent replacement of Oracle workflow in Oracle Forms Applications by SOA

Yesterday I did a presentation about a project I did last year, where we replaced Oracle Workflow in an Oracle Forms application by SOASuite.
Often Oracle Workflow standalone edition, is used closely integrated within an Oracle Forms/Reports application. This means that you'll run in several issues:

  1. How do you start the proces in SOASuite from your Forms or database triggers?
  2. How do you handle end-user interactions: create notifications/tasks to your end users?
  3. What to do with the process-block activity?
  4. What to do with the automatic activities: the custom application services you call from the workflow process?
  5. And how to transform your workflow process to BPEL?
Then there were two main issues:
  1. Reset & Restart functionality, a.k.a. Expedite in Workflow
  2. Long running database transactions
About the latter I wrote two articles how we solved that technically:
I tried to upload my presentation to SlideShare, but that did not work properly. But the pdf of the presentation can also be found on the website of the Oracle Usersgroup Holland.
For non-Dutch visitors: it's in Dutch.


Friday, 8 March 2013

Fusion Middleware Community Forum 2013

A few weeks ago I went to the Fusion Middleware Community Forum 2013. Unfortunately I wasn't in the opportunity to write anything about it, until now.

First it was great to meet many peers in our interest-area. Not only because it was in a splendid environment:
And we had a social event on tuesday in a little castle:

(Sorry my phone had a little difficulty with the little amout of light). And there we had a nice diner where I could sit beside one of the few women (Simone Geib) and a former co-worker from an earlier customer that I got re-acquainted with (Arnoud Roth). So I had to share my attention.



Anyway, there were many interesting subjects, but to name the ones that interested me the most:
  • BPM Suite PS6: what's new?
  • BPM Process Accelarators
  • B2B11g presentation
  • ADF11g Mobile demo, especially related to SOA and BPM
  • What's new in SOASuite, 'Fast Data' (in stead of Big Data), Cloud and Mobile.
  • SOASuite 12c sneak preview
  • Workshops on BPM Suite PS6
I'm not going to write now on everything. But I'll try to follow up later.

BPM Suite PS 6: what's new?

Well a lot. And they call it  'Model to Execution'. It occurred to me that in the latest patch sets not much changed on the SOA part. Actually it is considered stable. I've seen in the SOASuite12c sneak preview a lot of nice new features, but I can't write about that. However I must say that there were quite some things that I felt like waiting for them for about 6 or 7 years already. Let me put it this way: I did not like Cobol since it does not has functions. And although I do like BPEL, I do miss something like that.  

But on BPM11g a lot changed every Patch Set. PS5 was a major one, where amongst others we've got:
  • Correlation Sets, like we had in BPEL for years
  • Import of Oracle Workflow schema's (and I believe Visio?)
  • Alter Flow (change the route of an already running instance to an earlier or later activity)
  • And improvements in Process Composer
  • Mark activities as 'Draft': so to be implemented
Now in PS6 they put a lot of effort in Process Composer. What I do like in that tool is that it really enables you to have a joined modelling environment with your Business/Process Analysts. Earlier in BPM10g it turns out that the developers modelled the processes based on designs/functional requirements of the BA's delivered in MS Word documents. Now they can model them theirselves. The implementation, however, is done by the developers (as it should). Think about the screens, the services, etc.

Until now there were a few lacks in modelling in Process Composer:
  • Simulation had to be done in Studio: so in JDeveloper
  • Taskscreens had to be build in ADF by a developer
  • Execution could only be done after real deployment in the test environment
These are the major areas addressed in Composer. Now I don't have screendumps at hand. I could make them, but I'm not sure I'm allowed to publish them. As soon as I've got clearance or when it's GA (Global Available), I'll settle my self to install it and create pictures.
But let me brievely describe them.

Simulation can now be done in Process Composer. And that is a great thing, since now the Analyst can model the flow and see how it will behave, without waiting for a developer to import the model in JDeveloper and do the simulation. I think they ported the whole tool from BPM Studio to Composer, because it's all there: Assign resources, define probabilities on flow-paths, create simulation models, execute what-if's etc. 

Webforms: Oracle incorporated an external tool that allows you to define screens within Composer. You can declaritively define screens, with all the necessary artefacts (date, number, textfields, radiobuttons, etc.) including tabs, tables and repeatable areas, just by drag-and-drop on a canvas. If you're familiar with BPM10g or Aqualogic BPM 6 or before, then you might recognise the Business Object Presentations in it. Since it really does look like it, with a slightly different look&feel, I at first thought it was a port of that part of the product to the Composer. But actually it is an external product that Oracle incorporated in the tool (as far as I know they did not acquire it (yet)).  Now, the beauty of it is that it not only is a Mock that allows the BA to discuss with the developer on how the screen should look like in ADF. It is really a usable screen. Along as you build up your screen, it will create the underlying dataobjects that is needed to provide the screen with input and after submit will get you the output. But you can also use existing dataobjects from your process to build up your Webform. In the end it can really be used in production. It will be embedded in a ADF form with the basic controls of a BPM task. So you can leave it that way and use it in production in case it suffice. Or you can ask a developer to recreate the screen in ADF if you have additional requirements. But you don't have to wait for the end result of the developer. You're able to go further with your model, put it even in production and plug in the ADF form in a later stage (even after go-live!).

Question: can you edit the webforms in Studio? No, in Studio you still should use ADF to create your forms. When you import the project into BPM Studio, the Webforms artefacts will be imported in the project as a zip-file. With large signs "Do not open!" on it. Don't even think about it. We've been assured that the moment you open the zip, a personal email will go to the BPM Product managers and the development team... And you're excluded from further use... So now you've been warned.

Now, a developer could create a template project, with business objects definitions (basically xsd's that in my opion should reside in a structured way in the MDS) together with the service exposures and service references needed in a BPM process. Based on that a Process Analyst can create the complete flow, including all the service calls, task forms, completely ready to execute. And than comes the third great part: he can do it himself really in the Process Composer. There will be a run/test button that will wrap up the project and do a sort of background-test-deployment to a seperate test-domain/partition in the SOASuite. And then you can really step by step execute the flow. At each Webform you can choose to really execute it with a authorised user from the Organisation Model, or just choose  an outcome. Then the flow will go further executing all the service references and so on. 
Mark that it is really a sort of test deploy. It won't get deployed to the default or other partion of the SOASuite. For that you should import the project into Studio, version it in for instance Subversion and use your deployment framework to deploy it to the correct environment for further testing.

And that is Model to Execution with BPM Process Composer. I really think they did a great job. A Webforms editor in Studio would have been nice. Or a Webforms to ADF converter. Or even better: create an ADF alternative for the online Webforms-composer... But you'll find that BPM11g is coming more and more mature. I'm really looking forward to have my fingers on it and use it with a customer in the Netherlands. Any customer over here who is interested?

SOA/BPM Mobile

Lately two of my colleagues gave a presentation on our Teammeeting about ADF and Apex mobile. In fact they turned out to be right given the nice ADF Mobile demo by Mr. Forms Grant Ronald. He brought it well...
Now it brought me with the question: ADF Mobile is nice but how about BPM Mobile. Because there is where I see the most interesting area of mobile apps in the business. You have a business process and there are people on the road who are in fact executing your business model. For instance last week my dishwasher broke down (it really did) and we had to buy a new one. Now I could have installed it myself, if I would give myself the time. But then I had to bring a way the old one myself. So for a relatively small amount a mechanic was coming over to bring my new dishwasher, removed the old one and installed the new one. 
Would'nt it be great if he could get his order as a task in his iPhone or Android, together with the address and all the details, may be a pdf with the order attached? That he could link to the navigator to be routed to my address? And after the install just push a button to complete the task and get the new order?
After that the process could go further with for instance a complete order subprocess.

So I wondered if there were plans to create a mobile version of the BPM Worklist Application. It turns out there aren't any. What they do plan is to deliver REST services on the BPM API's. 
For now you could build those REST services your self. It's one of the things on my wish-list to figure out.
Then you could create a dedicated ADF-Mobile app calling those REST services. And then it should not be rocket science to have the app build. Thinking about it, maybe this is actually the way to go.
One of the major issues with ADF mobile (as I see) is the deployment. You have to have your hands on the phone that needs the app. There is no way to deploy it using a market-mechanism.

B2B11g

It was very good to have a slot about B2B11g. I think it is a hardly known, very under-lightened piece of SOASuite. That could really lighten much of the hard work of interacting with peer-companies or geographically dis-located enterprise-departments. Although to me it was not very  new. It was hosted by Simone Geib (product manager of SOASuite) but actually co-presented and demoed by Krishnaprem Bathia, someone I knew from my days at the Dutch Collection Bureau of the Department of Justice. Back then we worked closely together to get B2B10g with the ebXML adapter working for the customer. And it was nice to be able to ask him some questions.

One of the things I would put up for improvement is that in the current release you need to have an agreement for every document-exchange-capability with your trading partner. There is a generation button that allows you to create an agreement for every document that a trading-partner is configured to be allowed to receive or send. But then you might get a lot of agreements each having a validity period and for each you have to provide the correct identifiers and so on. It would be nice to be able to group multiple capabilities into a small number of agreements. So you could releate a group of capabilities to a certian project or delivery or contract that have a particular effectivenes period. So you could have a TP that will have some capabilities that from let's say april the first will be effective. Then there is a follow-up project that allows the TP to have additional capabilities starting from september the 1st. Be able to group them into an agreement will improve the insight in and the management of agreements of the TP.
Besides the generation button in the UI, you could also use the selfservice API's/Ant tasks of B2B, like I demonstrated in my earlier post

Last year I had a project with an energy net management company in the Netherlands. And it had to exchange messages with around 80 trading partners. Looking into all the URL's it seemed to me that no other used B2B11g. But it seems unlikely to me that no other uses SOASuite. So they have B2B11g in the house. 

BPM Process Accelarators

Last week I delivered the OPN bootcamp on AIA 11g. And the week before I saw this presentation and they seem to have some in common. However they are seperate initiatives within Oracle.
With BPM Process Accelerators they create standard solutions on common Business Processes. And they are shared (haven't heard about license costs), to be reused. They will include best practices on BPM Process Modelling and common usages. It seemed to me that the library of models is very filled at the moment. But it can be very helpful as a startup for many cases. Probably most companies have about the same processes like employee-hiring, procurement, declarations, etc. Although everyone does the job in their own way, it helps if you have a template on how someone else did it.

BPM11g PS6 Workshops

Well here we got to play with the Webforms, Simulator and Model Execution within Process Composer. Some presentations on use and bestpractices. Only a pity that there was little time. We had the whole thursday and friday morning. But in the afternoon we had to leave, while the presentors couldn't get enough of it. They're were good and many thanks to them. I hope I can finish the workshop soon completely and maybe deliver them myself. So, show your interest.

Friday, 1 February 2013

Sending and Saving Emails in SoaSuite

My current project is about the revisiting of some workflows built in SoaSuite10g (Bpel, Esb).
In these workflows emails are sent at several point. Some of these mails are about errors to the administrator. But others are for customers/applicatants or end-users.

In one of these cases  I have to add a new email that is to be saved in to the Document Management System (DMS) after being send.

Now in a typical use of the email-notification service, the text of the message is put in the Notification activity in the bpel process. That means that the text is fixed/hard-coded in the Bpel code. Changing the text means changing the bpel process, and have it through the whole test/acceptance/production cycle.

In this particular case the message is to be determined up-front, since I need to save the same message to file. One of my "rescues" is the lookupXML function I talked about in my previous post. The other one is the lovely XSL-language.

First I created an XML-file file as follows:

<?xml version="1.0" encoding="windows-1252" ?>
<!-- $Id: email_texts.xml 99 2013-02-01 08:27:07Z ca_mavandenakker $ -->
<!-- This file can be located in a HTTP accessible directory in the midtier installation and it's referred 
     through an lookup-xml XPath expression in the service invocation.  -->
<!-- The current location is http://${deploy.soasuite.URL}/ObjectLibrary/EmailService/xml/email_texts.xml -->
<email_texts>
   <email_text>
      <key>EmailSubject1</key>
      <value>Subject Email 1</value>
   </email_text>
   <email_text>
      <key>Body Email 1</key>
      <value>Dear Mailer, &lt;br/>
Thank you for sending these documents:&lt;br/>
&lt;table>

 &lt;tr>
  &lt;td colspan =&quot;2&quot;>
   ${ReceivedDocuments}
  &lt;td>
 &lt;/tr>
  &lt;tr>
  &lt;td>Indiener:&lt;/td>&lt;td>${ApplicantName}&lt;td>
 &lt;/tr>
 &lt;tr>
  &lt;td>Telefoonnummer:&lt;/td>&lt;td>${ApplicantPhone}&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>E-mailadres:&lt;/td>&lt;td>${ApplicantEmail}&lt;/td>
 &lt;/tr>
  
 &lt;tr>
  &lt;td colspan=&quot;2&quot;>&lt;br>&lt;b>Your appointed reprecentative:&lt;/b>&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>Naam:&lt;/td>&lt;td>${ReprecentativeFullname}&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>Telefoon:&lt;/td>&lt;td>${ReprecentativePhone}&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>E-mail:&lt;/td>&lt;td>${ReprecentativeUsername}@darwin-it.nl&lt;/td>
 &lt;/tr>
&lt;/table> 
&lt;br/>
Your application is taken in account.

Remark: this is an  automatically generated message. Replies on this message will not be answered.&lt;br/>
&lt;br/>
Best regards,&lt;br/>
&lt;br/>
Darwin-IT Professionals&lt;br/>
</value>
   </email_text>
</email_texts>

This file can be either on the file system of the SOAServer or on a particular folder in the docRoot of the HTTP server of the SoaServer. In 11g you would put it in the MDS.

As you can see in the example, I put several properties into the document. Properties in the form of ${property}, like properties in ANT.

These have to be replaced by the process giving a list of properties. I created a bpel process that does this. And use the result to send the email message. In the response the resulting subject and body are returned, so that they can be used to save to a html-document in a temporary folder on the filesystem.

The xsd of the bpel process is as follows:
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
            xmlns:svnId="$Id" xmlns:version="1.0"
            targetNamespace="http://www.darwin-it.nl/xsd/v1/EmailService"
            xmlns="http://www.darwin-it.nl/xsd/v1/EmailService"
            xmlns:ems="http://www.darwin-it.nl/xsd/v1/EmailService"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <xsd:element name="EmailServiceProcessRequest"
              type="ems:EmailServiceProcessRequestType"/>
 <xsd:element name="EmailServiceProcessResponse"
              type="ems:EmailServiceProcessResponseType"/>
 <xsd:complexType name="EmailServiceProcessRequestType">
  <xsd:sequence>
   <xsd:element name="FromAccountName" type="xsd:string" minOccurs="0"/>
   <xsd:element name="To" type="xsd:string"/>
   <xsd:element name="Cc" type="xsd:string" minOccurs="0"/>
   <xsd:element name="Bcc" type="xsd:string" minOccurs="0"/>
   <xsd:element name="ReplyToAddress" type="xsd:string" minOccurs="0"/>
   <xsd:element name="subjectKey" type="xsd:string"/>
   <xsd:element name="bodyKey" type="xsd:string"/>
   <xsd:element name="PropertyList" type="ems:PropertyList" minOccurs="0"/>
  </xsd:sequence>
 </xsd:complexType>
 <xsd:complexType name="EmailServiceProcessResponseType">
  <xsd:sequence>
   <xsd:element name="To" type="xsd:string"/>
   <xsd:element name="Cc" type="xsd:string" minOccurs="0"/>
   <xsd:element name="EmailBody" type="xsd:string"/>
   <xsd:element name="EmailSubject" type="xsd:string"/>
  </xsd:sequence>
 </xsd:complexType>
 <xsd:complexType name="PropertyType">
  <xsd:sequence>
   <xsd:element name="name" type="xsd:string"/>
   <xsd:element name="value" type="xsd:string"/>
  </xsd:sequence>
 </xsd:complexType>
 <xsd:element name="Property" type="ems:PropertyType"/>
 <xsd:complexType name="PropertyList">
  <xsd:sequence>
   <xsd:element name="Property" type="ems:PropertyType" maxOccurs="unbounded"/>
  </xsd:sequence>
 </xsd:complexType>
</xsd:schema>

The subjectKey and bodyKey elements correspond to the key values in the email.xml. With those values the XSL will lookup the particular template text. In the process the following xsl is used to do the transformation:
<?xml version="1.0" encoding="UTF-8" ?>
<?oracle-xsl-mapper
  <!-- SPECIFICATION OF MAP SOURCES AND TARGETS, DO NOT MODIFY. -->
  <mapSources>
    <source type="XSD">
      <schema location="EmailService.xsd"/>
      <rootElement name="EmailServiceProcessRequest" namespace="http://www.darwin-it.nl/xsd/v1/EmailService"/>
    </source>
  </mapSources>
  <mapTargets>
    <target type="XSD">
      <schema location="EmailService.xsd"/>
      <rootElement name="EmailServiceProcessResponse" namespace="http://www.darwin-it.nl/xsd/v1/EmailService"/>
    </target>
  </mapTargets>
  <!-- GENERATED BY ORACLE XSL MAPPER 10.1.3.5.0(build 090730.0200.1754) AT [WED JAN 30 10:49:55 CET 2013]. -->
?>
<xsl:stylesheet version="1.0" xmlns:svnId="$Id"
                xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
                xmlns:ehdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.esb.server.headers.ESBHeaderFunctions"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:version="1.0"
                xmlns:hwf="http://xmlns.oracle.com/bpel/workflow/xpath"
                xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20"
                xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ora="http://schemas.oracle.com/xpath/extension"
                xmlns:ids="http://xmlns.oracle.com/bpel/services/IdentityService/xpath"
                xmlns:orcl="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc"
                xmlns:ns0="http://www.darwin-it.nl/xsd/v1/EmailService"
                exclude-result-prefixes="xsl svnId xsd version ns0 bpws ehdr hwf xp20 xref ora ids orcl">
  <xsl:variable name="configURI"
                select="'http://melia03.hhdelfland.nl/ObjectLibrary/EmailServices/xml/email_texts.xml'"/>
  <xsl:variable name="test"/>
  <xsl:template match="/">
    <ns0:EmailServiceProcessResponse>
      <ns0:To>
        <xsl:value-of select="/ns0:EmailServiceProcessRequest/ns0:To"/>
      </ns0:To>
      <ns0:Cc>
        <xsl:value-of select="/ns0:EmailServiceProcessRequest/ns0:Cc"/>
      </ns0:Cc>
      <ns0:EmailBody>
        <xsl:call-template name="getEmailText">
          <xsl:with-param name="key"
                          select="/ns0:EmailServiceProcessRequest/ns0:bodyKey"/>
          <xsl:with-param name="propList"
                          select="/ns0:EmailServiceProcessRequest/ns0:PropertyList"/>
        </xsl:call-template>
      </ns0:EmailBody>
      <ns0:EmailSubject>
        <xsl:call-template name="getEmailText">
          <xsl:with-param name="key"
                          select="/ns0:EmailServiceProcessRequest/ns0:subjectKey"/>
          <xsl:with-param name="propList"
                          select="/ns0:EmailServiceProcessRequest/ns0:PropertyList"/>
        </xsl:call-template>
      </ns0:EmailSubject>
    </ns0:EmailServiceProcessResponse>
  </xsl:template>
   
  <!--  User Defined Templates  -->
   
  <xsl:template name="getEmailText">
    <xsl:param name="key"/>
    <xsl:param name="propList"/>
    <xsl:variable name="emailTextTpl"
                  select='orcl:lookup-xml($configURI,"/email_texts/email_text","key","value",$key)'/>
    <xsl:variable name="emailText">
      <!-- <xsl:value-of select="$emailTextTpl"/> -->
      <xsl:call-template name="replaceProps">
        <xsl:with-param name="string" select="$emailTextTpl"/>
        <xsl:with-param name="propList" select="$propList"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$emailText"/>
  </xsl:template>
   
  <xsl:template name="replaceProps">
    <xsl:param name="string"/>
    <xsl:param name="propList"/>
    <xsl:variable name="parm">
      <xsl:call-template name="getFirstProp">
        <xsl:with-param name="string" select="$string"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="parmVal"
                  select="$propList/ns0:Property[ns0:name=$parm]/ns0:value"/>
    <xsl:variable name="newString">
      <xsl:choose>
        <xsl:when test="string-length($parm)>0">
          <!-- There is a parameter so recusively call replaceProps with a replaced string -->
          <xsl:call-template name="replaceProps">
            <xsl:with-param name="string">
              <!-- Replace the parameter with the value and use that as a parameter for the recursively called template -->
              <xsl:call-template name="replace-ci">
                <xsl:with-param name="input" select="$string"/>
                <xsl:with-param name="fromStr" select="concat('${',$parm,'}')"/>
                <xsl:with-param name="toStr" select="$parmVal"/>
              </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="propList" select="$propList"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <!-- No parameters left so just return the string -->
          <xsl:value-of select="$string"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$newString"/>
  </xsl:template>
   
  <xsl:template name="replace-ci">
    <xsl:param name="input"/>
    <xsl:param name="fromStr"/>
    <xsl:param name="toStr"/>
    <xsl:if test="string-length( $input ) > 0">
      <xsl:variable name="startPos">
        <xsl:call-template name="index-of-ci">
          <xsl:with-param name="string" select="$input"/>
          <xsl:with-param name="search" select="$fromStr"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="inputLwr" select="xp20:lower-case($input)"/>
      <xsl:variable name="fromStrLwr" select="xp20:lower-case($fromStr)"/>
      <xsl:choose>
        <xsl:when test="contains( $inputLwr, $fromStrLwr )">
          <xsl:variable name="stringBefore"
                        select="substring($input,1,$startPos - 1)"/>
          <xsl:variable name="stringAfter"
                        select="substring($input,$startPos + string-length($fromStr))"/>
          <xsl:value-of select="concat($stringBefore,$toStr)"/>
          <xsl:call-template name="replace-ci">
            <xsl:with-param name="input" select="$stringAfter"/>
            <xsl:with-param name="fromStr" select="$fromStr"/>
            <xsl:with-param name="toStr" select="$toStr"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$input"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>
   
  <xsl:template name="index-of-ci">
    <xsl:param name="string"/>
    <xsl:param name="search"/>
    <xsl:param name="startPos"/>
    <xsl:variable name="searchLwr" select="xp20:lower-case($search)"/>
    <xsl:variable name="work">
      <xsl:choose>
        <xsl:when test="string-length($startPos)>0">
          <xsl:value-of select="substring($string,$startPos)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$string"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="stringLwr" select="xp20:lower-case($work)"/>
    <xsl:variable name="result">
      <xsl:choose>
        <xsl:when test="contains($stringLwr,$searchLwr)">
          <xsl:variable name="stringBefore">
            <xsl:value-of select="substring-before($stringLwr,$searchLwr)"/>
          </xsl:variable>
          <xsl:choose>
            <xsl:when test="string-length($startPos)>0">
              <xsl:value-of select="$startPos +string-length($stringBefore)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="1 + string-length($stringBefore)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          -1
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:copy-of select="$result"/>
  </xsl:template>
   
  <xsl:template name="getFirstProp">
    <xsl:param name="string"/>
    <xsl:variable name="startPosParm">
      <xsl:call-template name="index-of-ci">
        <xsl:with-param name="string" select="$string"/>
        <xsl:with-param name="search" select="'${'"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="endPosParm">
      <xsl:call-template name="index-of-ci">
        <xsl:with-param name="string" select="$string"/>
        <xsl:with-param name="search" select="'}'"/>
        <xsl:with-param name="startPos" select="$startPosParm"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="parmNameLength"
                  select="number($endPosParm)-(number($startPosParm)+2)"/>
    <xsl:variable name="parm">
      <xsl:choose>
        <xsl:when test="number($startPosParm)>0 and number($parmNameLength)>0">
          <xsl:value-of select="substring($string, $startPosParm+2,$parmNameLength)"/>
        </xsl:when>
        <xsl:otherwise/>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$parm"/>
  </xsl:template>
</xsl:stylesheet>

You might think to just loop over the properties in the property list and then replace all the corresponding properties with the corresponding values. But that won't work. You can't repeatedly change a variable or target in XSL. The trick to solve this is recursion.

The template first looks up the email text, in the "getEmailText" template, using the same lookupXML function as used in my previous post. Then it replaces all occurences of properties from the template text using the "replaceProps" template. That template does a lookup of the first occurence of the parameter within the text, using the "getFirstProp"-template. If it finds one, it will do a replace of the property with the value and feed the result recursively into the "replaceProps"-template. The value is queried using the key from the propertylist  If it does not find a property then it will return just the text, which ends the recursion.

The lookup and replacement is done with about the same functions I described in this post. Unfortunately I found a bug in the replace template in that post.  I solved it in the version above, where I also left-out a small un-needed functionality. Maybe you can find it as well?

I won't explain the rest of the process. After the transformation above it just fills in the blanks in the notification service using the elements above. The property list is a simle name-value-pair list. To fill that is not so hard either.

LookUp DVM in SoaSuite 10g, the 11g way

In SoaSuite 10g there is a xpath and xslt function do Domain Value Map lookups,lookupDVM. This function is based on tables in the SoaSuite repository. There is a screen in the ESB console to edit them.

In SoaSuite 11g there is a separate artefact for DVM's, and a corresponding lookupDVM function.
I found that one more convenient, since there is an editor in Jdeveloper for it and the artefact is in fact an xml file that can be put in the MDS and referenced from anywhere in your SOA projects. And lastly, it can be versioned as any other artefact.
Deployment is similar to other SOA-artefacts. For 10g you must ensure to synchronise the dvm's in the database between the different (OTAP) environments.

Today I needed a simple translation from 0,1 to Ja,Nee (Yes, No). I did not feel like to create a DVM in the database. But, also in 10g there is a quite similar solution as the 11g lookupDVM function

In SoaSuite 10g, there is an oracle xsl-extension function: lookupXML. It enables you to read in a xml file and perform a xpath-lookup query. I found it very convenient to lookup fixed (OTAP) environment-dependent parameters. If you would put those in bpel preferences you have to ensure that they're changed every deployment between the environments. Using the lookupXML function, you can lookup those values from an xml file from the filesystem of the SOASuite. Then you only have to put an version of the file in a fixed folder on every SOASuite environment. After adapting those for the particular environment, you don't need to touch it every deployment anymore.

This same construction can be used to do lookups. I created the following xml file:

<?xml version="1.0" encoding="windows-1252" ?>
<!-- $Id: email_texts.xml 99 2013-02-01 08:27:07Z ca_mavandenakker $ -->
<!-- This file can be located in a HTTP accessible directory in the midtier installation and it's referred 
     through an lookup-xml XPath expression in the service invocation.  -->
<!-- The current location is http://${deploy.soasuite.URL}/ObjectLibrary/Core/xml/v1/CoreLookupDVM.xml -->
<domains>
      <domain name="JaNee">
            <keyDb>0</keyDb>
            <dispValue>Nee</dispValue>
            <boolean>false</boolean>
      </domain>
      <domain name="JaNee">
            <keyDb>1</keyDb>
            <dispValue>Ja</dispValue>
            <boolean>true</boolean>
      </domain>
</domains>
   
Since the lookupXML is a xsl function, it is in 10g apparently not available as an Xpath function, I created an XSD for the convenience of doing the DVM-lookups:
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:version="1.0"
            xmlns:svnId="$Id$" xmlns="http://www.darwin-it.nl/xsd/v1/DVM"
            xmlns:dvm="http://www.darwin-it.nl/xsd/v1/DVM"
            targetNamespace="http://www.darwin-it.nl/xsd/v1/DVM"
            elementFormDefault="qualified">
  <xsd:element name="DVM" type="dvm:DVMType">
    <xsd:annotation>
      <xsd:documentation>Domain Value Mapping</xsd:documentation>
    </xsd:annotation>
  </xsd:element>
  <xsd:complexType name="DVMType">
    <xsd:sequence>
      <xsd:element name="Domain" type="xsd:string"/>
      <xsd:element name="FromKey" type="xsd:string"/>
      <xsd:element name="FromValue" type="xsd:string"/>
      <xsd:element name="ToKey" type="xsd:string"/>
      <xsd:element name="ToValue" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>
Based on this XSD you can create a variable, fill in the Domain, eg. "JaNee", the FromKey, eg. "keyDb", the FromKeyValue, eg. "0", and the ToKey, eg. "dispValue" or "boolean". Then you can transform the variable to it's self using the following XSL:
<?xml version="1.0" encoding="UTF-8" ?>
<?oracle-xsl-mapper <!-- SPECIFICATION OF MAP SOURCES AND TARGETS, DO NOT MODIFY. -->
  <mapSources>
    <source type="XSD">
      <schema location="http://melia03.hhdelfland.nl/ObjectLibrary/Core/xsd/v1/DVM.xsd"/>
      <rootElement name="DVM" namespace="http://www.darwin-it.nl/xsd/v1/DVM"/>
    </source>
  </mapSources>
  <mapTargets>
    <target type="XSD">
      <schema location="http://melia03.hhdelfland.nl/ObjectLibrary/Core/xsd/v1/DVM.xsd"/>
      <rootElement name="DVM" namespace="http://www.darwin-it.nl/xsd/v1/DVM"/>
    </target>
  </mapTargets>
  <!-- GENERATED BY ORACLE XSL MAPPER 10.1.3.5.0(build 090730.0200.1754) AT [FRI FEB 01 10:04:09 CET 2013]. -->
?>
<xsl:stylesheet version="1.0"
                xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
                xmlns:ehdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.esb.server.headers.ESBHeaderFunctions"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:version="1.0"
                xmlns:ns0="http://www.darwin-it.nl/xsd/v1/DVM"
                xmlns:hwf="http://xmlns.oracle.com/bpel/workflow/xpath"
                xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20"
                xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ora="http://schemas.oracle.com/xpath/extension"
                xmlns:ids="http://xmlns.oracle.com/bpel/services/IdentityService/xpath"
                xmlns:orcl="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc"
                xmlns:svnId="$Id$"
                exclude-result-prefixes="xsl xsd version ns0 svnId bpws ehdr hwf xp20 xref ora ids orcl">
  <xsl:variable name="configURI"
                select="'http://${deploy.soasuite.URL}/ObjectLibrary/Core/xml/v1/CoreLookupDVM.xml'"/>
                
  <xsl:template match="/">
    <ns0:DVM>
      <ns0:Domain>
        <xsl:value-of select="/ns0:DVM/ns0:Domain"/>
      </ns0:Domain>
      <ns0:FromKey>
        <xsl:value-of select="/ns0:DVM/ns0:FromKey"/>
      </ns0:FromKey>
      <ns0:FromValue>
        <xsl:value-of select="/ns0:DVM/ns0:FromValue"/>
      </ns0:FromValue>
      <ns0:ToKey>
        <xsl:value-of select="/ns0:DVM/ns0:ToKey"/>
      </ns0:ToKey>
      <ns0:ToValue>
        <xsl:call-template name="LookupDVM">
          <xsl:with-param name="domainName" select="/ns0:DVM/ns0:Domain"/>
          <xsl:with-param name="fromKey" select="/ns0:DVM/ns0:FromKey"/>
          <xsl:with-param name="fromKeyValue" select="/ns0:DVM/ns0:FromValue"/>
          <xsl:with-param name="toKey" select="/ns0:DVM/ns0:ToKey"/>
        </xsl:call-template>
      </ns0:ToValue>
    </ns0:DVM>
  </xsl:template>
  <!--  User Defined Templates  -->
  <xsl:template name="LookupDVM">
    <xsl:param name="domainName"/>
    <xsl:param name="fromKey"/>
    <xsl:param name="fromKeyValue"/>
    <xsl:param name="toKey"/>
    <xsl:variable name="parentPath"
                  select="concat('/domains/domain[@name=&quot;',$domainName,'&quot;]' )"/>
    <xsl:variable name="result"
                  select="orcl:lookup-xml($configURI,$parentPath,$fromKey,$toKey ,$fromKeyValue)"/>
    <xsl:value-of select="$result"/>
  </xsl:template>
</xsl:stylesheet> 
This for those cases that you just want to lookup some values from the domain-xml this construction can be handy. In other cases where you need to lookup multiple values in an complexer xsl't you can copy and paste the construction above.

Since the CoreLookupDVM.xml is a, quite simple, xml file, it can easily be transformed to an 11g lookupDVM file.

Thursday, 10 January 2013

Google Docs: the clipboard-sharing tool in the cloud

Today I was asked to support a customer with some urgent issues. To be able to help them quickly I was given access via a VMware View Client (a Citrix alike online desktopsharing solution from VMware).
From that desktop I connected to an internal desktop with a Remote Desktop session, since there were all the necessary tools, like Notepad++, jDeveloper, SoapUI, etc, installed. In the end I neede another RDP session to the server on which SoaSuite was running.

Anyway, during the analasis I kept a report document in Google Docs. At one point I needed to write an email in which I wanted to share some information from logs on the remote desktop. Now I could connect to gmail. But I was already writing the email in Thunderbird on my laptop. Since there were so may layers of RDP-in-RDP sessions, I could not copy and paste from the logs to my laptop anymore.

Then the team-cooperation functionality from Google Docs come in handy! I opened the same document again on my laptop and Google Docs neatly synchronized both sessions. So anything I wrote and copied-and-pasted into the document in the RDP session became visible on my laptop and vice-versa. Now, I know: it's not new, but it enabled me to copy-and-paste from the document into my email.

So, Google Docs turned out the perfect clipboard sharing tool. I found it a nice trick to share.