Wednesday, 24 March 2010

Postfix for handling mail in your integration solution

Sometimes there is a need for integrating with mail. You could say: that's easy, since we could use the notification service in BPEL as described here.
However, this solution requires that there is a mail-box to connect to. But what if you want to serve multiple (10s, 100s or even 1000s of) e-mail-addresses within a certain domain? Maybe multiple sub-domains within a certain main-domain? Then you would not want to create seperate mail-boxes for each address. That would give to much of an administration. Not only you have to create new mail-boxes, but also have activation agents for each of this mail-box. That would have an enormous performance pressure. So, what then?

In my previous post on handling email with SoaSuite (BPEL), I already mentioned Apache James as an email server. Nearly every Linux distribution also comes with Postfix. Postfix is a Mail Transfer Agent. It's not an e-mail server like James. It handles SMTP-messages. It will listen for SMTP-trafic and filter each message to determine if it has to be passed on to another MTA or to be handled locally. If a message has to be handled locally, it can be stored to local mailboxes or a local POP or IMAP server, for example. But it is also quite easy to let Postfix call a local script or executable. And that for mail-addresses that meet certain filter rules, like belonging to a domain. This script can then be used to have the message put/enqueued on a queue. The script can also be used to enhance the email messages with properties from the SMTP-envelope. I added that in the example too.

Now I'm not an e-mail server expert. But I had to figure out how to configure Postfix for this use-case. And although Postfix seem complicated at first sight, this turns out remarkebally easy.

Postfix installation
I choose Oracle Enterprise Linux 5 Update 4, as an alternative to Red Hat. But about any Linux-taste would do. Provided that it has a Postfix-pacYou might uninstall Sendmail (or unselect it in the package-options on install of the OS). This to prevent collision with the Postfix functionality.

Although you would do al the configuration as root (postfix runs as root), it is strongly advised to introduce an extra user for the message-handling. To own the scripts, etc.
In my setup the following folders are created:

Folder

Meaning

/etc/postfix

Configuration files and lookup tables

/usr/libexec/postfix

Postfix daemons

/var/spool/postfix

Queue files

/usr/sbin

Postfix commands


This is a quite common setup, but in other (Linux or Unix) distributions the folder locations might differ slightly.

Postfix Usecase
This schema provides an overview on how Postfix will work. Left Incoming (smtp) messages will be picked up by the Smtp-deamon. Then via "Cleanup" it will be handed over to the QueueManager, via the incoming queue. The Queuemanager will put the message on the Active queue. From there it will be picked up by either the smtp-service to forward the mail to the smtp-server on the infrastructure of your Internet Service Provider or your company. Using MA-records of the DNS-server it will determine to which smtp-server it has to be send actually.
In our use-case the pipe deamon is the interesting one. This is the one we're going to instruct to call a script.

Basic Settings
Hostname
The hostname command can be used to determine the fully qualified name. This is the one used by Postfix to determine the host. If this does not include the domain-name then the following command can be used to instruct Postfix what the FQN should be:
postconf -e myhostname=vmsmtp.darwin-it.local


Mydestination
This parameter can be left default. But if mail have to be accepted for certain domains and to be delivered using the local transport, set the following parameter in main.cf:

# 2010-02-03, M. vd. Akker: To have mail accepted for .darwin-it.local
mydestination = $myhostname, localhost.$mydomain, localhost, .darwin-it.local

For now leave it default:
mydestination = $myhostname, localhost.$mydomain, localhost


Network interfaces
To have mail from external networks, non-localhost, the inet_interfaces must be set in main.cf. In my case I run Postfix within a Virtual Machine. And I want to use my Thunderbird to send mail to Postfix. So first Postfix has to be told from which network-devices it should accept mail from. Since my VM is "hidden", for simplicity we accept mail from all network devices. But this can be narrowed down.


# RECEIVING MAIL

# The inet_interfaces parameter specifies the network interface
# addresses that this mail system receives mail on.  By default,
# the software claims all active interfaces on the machine. The
# parameter also controls delivery of mail to user@[ip.address].
#
# See also the proxy_interfaces parameter, for network addresses that
# are forwarded to us via a proxy or network address translator.
#
# Note: you need to stop/start Postfix when this parameter changes.
#
# 2010-02-10, M. van den Akker: Setup all interfaces for excepting mail.
inet_interfaces = all
#inet_interfaces = $myhostname
#inet_interfaces = $myhostname, localhost
#inet_interfaces = localhost



Specific domains to accept mail from are set with mynetworks parameters. To select sub-domains to accept mail from, change the mynetwork parameter to accept mail from your host for example.
# TRUST AND RELAY CONTROL

# The mynetworks parameter specifies the list of "trusted" SMTP
# clients that have more privileges than "strangers".
#
# In particular, "trusted" SMTP clients are allowed to relay mail
# through Postfix.  See the smtpd_recipient_restrictions parameter
# in postconf(5).
#
# You can specify the list of "trusted" network addresses by hand
# or you can let Postfix do it for you (which is the default).
#
# By default (mynetworks_style = subnet), Postfix "trusts" SMTP
# clients in the same IP subnetworks as the local machine.
# On Linux, this does works correctly only with interfaces specified
# with the "ifconfig" command.
#
# Specify "mynetworks_style = class" when Postfix should "trust" SMTP
# clients in the same IP class A/B/C networks as the local machine.
# Don't do this with a dialup site - it would cause Postfix to "trust"
# your entire provider's network.  Instead, specify an explicit
# mynetworks list by hand, as described below.
#
# Specify "mynetworks_style = host" when Postfix should "trust"
# only the local machine.
#
#mynetworks_style = class
#mynetworks_style = subnet
#mynetworks_style = host

# Alternatively, you can specify the mynetworks list by hand, in
# which case Postfix ignores the mynetworks_style setting.
#
# Specify an explicit list of network/netmask patterns, where the
# mask specifies the number of bits in the network part of a host
# address.
#
# You can also specify the absolute pathname of a pattern file instead
# of listing the patterns here. Specify type:table for table-based lookups
# (the value on the table right-hand side is not used).
#
# 2010-02-10, M. van den Akker: Setup for accepting mail from CMI and localhost.
mynetworks = 192.168.192.0/28, 127.0.0.0/8
#mynetworks = 168.100.189.0/28, 127.0.0.0/8
#mynetworks = $config_directory/mynetworks
#mynetworks = hash:/etc/postfix/network_table

Where 192.168.192.0/28 should be replaced by the address-range of the host(s) from which you want to be able to receive email.

Start and stop postfix
Postfix can be stopped by:
[root@vmsmtpserver postfix]# postfix stop
Postfix can be started by:
[root@vmsmtpserver postfix]# postfix start
If it’s not already running.

After making changes to the configuration, Postfix has to be told to reload the configuration:
[root@vmsmtpserver postfix]# postfix reload
postfix/postfix-script: refreshing the Postfix mail system
Incoming e-Mail
Postfix will have to be configured that *.darwin-it.local will be seen as a domain for virtual mailbox addresses. The virtual transport has to be configured that these messages are handled by the pipe-deamon to call an external script.

We'll instruct Postfix also to enrich the smtp-message with two Custom properties:
  • x-envelope-to: Recipient list from the SMTP-Header
  • x-envelope-from: From-email-addres from the SMTP Header
Actually we implement the enrichment in the script. But Postfix will pass these properties as parameters.

Create a transport
To have incoming mail for .darwin-it.local transported to the script, a new transport has to be configured.

This is done in the master.cf file:
# 2010-02-10, M. van den Akker: Setup transport routescript for passing message to a bash-script
routescript   unix  -       n       n       -       -       pipe
flags=FDq. user=smtpuser argv=/bin/bash -c /home/smtpuser/routescript.sh -s $sender -r $recipient -q $nexthop -

Here a new transport is created, named “routescript”. It refers to the “pipe” deamon.

The “flags=FDq” denote that the From and destination addresses from the header are added to the header.
Change the following if necessary:
  • The user denotes the unix-user that is used to run the script. In this case the unix-user “smtpuser” is used. Change it to the proper user (other than “root” or “postfix” ) .
  • argv: denotes in this case that a bash script is called. Bash is called to run the routescript.sh script, that is placed in the home folder of the smtpuser. Change the path according to the correct location of the given script.
  • The next parameters are parameters for the routemq.sh script:
    • -s: the sender of the message, $sender refers to the from-address on the smtp-envelope
    • -r: the recipient(s) of the message, $recipient refers to the to-list on the smtp-envelope
    • -q: the queue on which the message has to be put. Our script was designed to queue the message on IBM mq. The property $nexthop refers to the nexthop property in the transport map. Having used this parameter to denote the particular queue enables us to change only the transport map if a queue is changed. Or reuse this transport for different queues in different transport mappings. This is a nice way to pass info about the actual/technical transport channel to be used.
Create transport map
To route the '.darwin-it.local' domain to the script a transport map has to be made in the transport file.

Add the following line to the transport map file:
# 2010-02-10, M. van den Akker: Setup transport routescript for handling darwin-it.local-messages.
.darwin-it.local routescript:queuename
Where queuename is the queue that is used for the smtp-messages. This is the 'nexthop'-parameter where the $nexthop property in the master.cf refers to.

After having changed the transport map file, it has to be compiled into an indexed binary using the postmap tool. So execute the following command:
[root@vmsmtpserver postfix]# postmap transport
Define transport map
To have the transport map used by Postfix, add the following lines to the main.cf :
# 2010-02-10, M. van den Akker: use transport map
transport_maps=hash:/etc/postfix/transport

Routescript
Make sure that the routescript.sh as given in the appendix is placed on the location as defined in the master.cf above. In the example above it is: /home/smtpuser/routescript.sh
Make it owned by the user that is used by Postfix to execute the script (smtpuser).
Then make the script executable:
[smtpuser@vmsmtpserver postfix]$ chmod +x routescript.sh


The actual script as an example is given at the end of this article.

Outgoing e-Mail
All outgoing e-mail should be forwarded to your companies or ISP's smpt-infrastructure.

To do so set the relay-host parameter to the particular smtp-server in main.cf:
# INTERNET OR INTRANET

# The relayhost parameter specifies the default host to send mail to
# when no entry is matched in the optional transport(5) table. When
# no relayhost is given, mail is routed directly to the destination.
#
# On an intranet, specify the organizational domain name. If your
# internal DNS uses no MX records, specify the name of the intranet
# gateway host instead.
#
# In the case of SMTP, specify a domain, host, host:port, [host]:port,
# [address] or [address]:port; the form [host] turns off MX lookups.
#
# If you're connected via UUCP, see also the default_transport parameter.
#
#relayhost = $mydomain
#relayhost = [gateway.my.domain]
#relayhost = [mailserver.isp.tld]
#relayhost = uucphost
#relayhost = [an.ip.add.ress]
Restart Postfix
After having made the necessary changes above it is important to restart (stop and sart) Postfix. Just doing a reload of the config will probably not suffice.

Conclusion
It took me some time to understand Postfix. I was quite overwhelmed by the options. And it took me some time to figure out how to configure it for this particular usecase. Where I had to consult a co-worker (the one that sort of made up this use-case; thanks to Hugo). But as with most other things: after all it turns out to be simple. And it might be useful for many other cases.

Appendix: the routescript.sh
Below the script is given to be called by Postfix to route the messages. This script is designed to either output the message to a file or to an IBM mq client. The mq-client has to be installed from a licensed installment of IBM. IBM also provides the MA01-support pack, in which for several OS'es an compiled executable is provided. This executable (simply called 'q') provides a commandline interface to the IBM mq-client. The mq-client can put the message to a queue, but also to standard out. This is handy for testing, where no mq-client is available.

There are 2 lines to edit:
Q=~/bin/ma01/q : give here the proper path the q-executable from the MA01-support pack
fi|"$Q" -O "$QUEUE_NAME" : here the output of the if-block is outputted to the q-client using the queue-name. If the queue is not available in test-environment this will give an error. For test purposes it might be usefull to comment this line use (uncomment) either the line '#fi|"$Q" -s' to have the q-client output the message to STDOUT, or '#fi>$FILENAME' to have the output directed to file.

The script will exit with the result code of the last command, which is the q-client. If that one fails, the script with exit with the result-code telling Postfix to consider the call failed. Postfix will consider the message undelivered and retry it later.

#!/bin/bash
###################################################################################################
# Route messages.
# Script to route an email message, read from pipe and output it to a channel.
#
# author: Martien van den Akker
# (C) januari 2010
# Darwin-IT Professionals
###################################################################################################
#Declartions
E_WRONG_ARGS=85;
NUMBER_OF_EXPECTED_ARGS=6;
FILENAME=/tmp/routemq-`date +%Y%m%d-%H%M%S.%N`
Q=~/bin/ma01/q
TRUE=1
HDR_ENV_FROM="x-envelope-from"
HDR_ENV_TO="x-envelope-to"

#Function to display usage
usage(){
SCRIPT_PARAMETERS="-r receiver -s sender -q queuename";
USAGE="Usage: `basename $0` $SCRIPT_PARAMETERS";
echo $USAGE;
}

# Check Arguments
if [ $# -lt $NUMBER_OF_EXPECTED_ARGS ]
then
usage
exit $E_WRONG_ARGS;
fi
until [ -z "$1" ]
do
case $1 in
"-s") SENDER=$2;;
"-r") RECEIVER=$2;;
"-q") QUEUE_NAME=$2;;
* ) usage;
exit $E_WRONG_ARGS;;
esac;
shift 2;
done;

#echo header variables and cat stdin to output.
#Edit next line to give the proper full path to the “q”-executable from the MA01-support-pack
if [ "$TRUE" ]
then
echo "$HDR_ENV_FROM: $SENDER"
echo "$HDR_ENV_TO: $RECEIVER"
cat -
fi|"$Q"  -O "$QUEUE_NAME" #  uncomment to output to $QUEUENAME (Comment previous line)
#fi|"$Q" -s # only output to stdout
#fi>$FILENAME #uncomment to output to filename

exit $? #Exit with result-code of last command, which is the "q" command.

Wednesday, 10 March 2010

Apache James on IBM AIX

Apache James is a very nice e-mail server to be used in a development or test environment, where you need to integrate with an email system. I mentioned it in an earlier post. It supports smtp, pop, nntp (news) and imap.
It's installation is as simple as can be: just unzip the tool, set your JAVA_HOME environment variable and run the appropriate run.sh or run.bat script (given your OS being either Unix/Linux or Windows). The only thing you need besides the zip is a Java Runtime Environment that is at least of version 1.4.2.

For most systems this is all you have to do to get it running. But I found that on IBM AIX (5.x) it is a little less obvious. Getting it running is not an issue, but as soon as you want to add a user, you'll run into the error:
Exception: Security error: java.security.NoSuchAlgorithmException: SHA MessageDigest not available
And after that the telnet connection is closed.
It turns out that the security-provider packages are not registered properly. To get it right there are two things to do.
  1. Make sure that the JAVA_HOME is pointing to the jre folder in the root folder of the java-installment on your system. So like: /usr/java5_64/jre instead of /usr/java5_64. Also make sure that there is a lib/ext folder (/usr/java5_64/jre/lib/ext) that contains a <make>jceprovider.jar, eg. sunjce_provider.jar or ibmjceprovider.jar.
  2. Change <james-home>/bin/phoenix.sh to register the extensions:
  • Find the line:
JVM_EXT_DIRS="$PHOENIX_HOME/lib:$PHOENIX_HOME/tools/lib"
  • Change it to:
JVM_EXT_DIRS="$PHOENIX_HOME/lib:$JAVA_HOME/lib/ext:$PHOENIX_HOME/tools/lib"


Now james can be started using the run.sh/run.bat scripts and using the telnet console you should be able to succesfully add users.

It might be that on your system, the system administrators block port 25(smtp) and 110 (pop). That would prevent james to startup the smtp and pop services.
In the <james-home>/apps/james/SAR-INF/ there is a config.xml file. In that file you can find a line:
<pop3server enabled="true">

There you can choose to disable pop by changing the enabled attribute. But benaath that line there is a port element. You could change that instead to for example 8110. That would enable pop-support on the 8110 port. You should instruct your client to use that port off course.
The same counts for smtp-support. That can be found at:
<smtpserver enabled="true">

For smtp you could choose to set the port to 8025.

Thursday, 28 January 2010

Oracle Sun now definitely one team

Today I read the news that the European Commity approved the Sun acquisition without further demands. See webwereld.nl. Actually it is already a week ago. But today I read also the plans of a Web Open Office, or Open Web Office. Or Open Cloud Office. An alternative of Google's Docs. I'm very curious how that would look like and how it will compare to Google Docs. Open Office is a fairly mature Office Suite. So I'm looking forward to see which OpenOffice features will find their way in to the Oracle Cloud.

Further more the clouds are cleared upon MySql.

Another interesting statement I found was that Java SE should support more languages. Probably as an answer to the .Net world where you can develop in multiple languages. So lots of promisses that makes it interesting to "keep an eye in the sail" with Oracle (famous Dutch saying).

Monday, 18 January 2010

Application Server Connection in JDeveloper 10.1.3.x

This is actually a tip from the dusty old box, although I imagine that there are still people around with SoaSuite/Jdeveloper 10g. I got a VMware image with SoaSuite10133. And I couldn't connect to it with JDeveloper from outside the VM. And I should have the solution somewhere but I couldn't reproduce it. The solution lies in the opmn.xml found in $ORACLE_HOME/opmn/conf.

There you'll find a notification-server element. It propbably does not have the ipaddr node below it by default. But add it like below with 0.0.0.0 as ip-addresses. Doing so enables you to connect with a jdeveloper on a remote machine.
<notification-server inter>
<ipaddr remote="0.0.0.0" request="0.0.0.0"/>
<port local="6100" remote="6200" request="6003"/>
<ssl enabled="true" wallet-file="$ORACLE_HOME/opmn/conf/ssl.wlt/default"/>
</notification-server>

I wrote about connecting to an Oracle Application Server/SoaSuite within a VM in greater detail here, but I did not include the actual opmn.xml snippet like above.

Friday, 15 January 2010

It's back: the Business Event System

My first steps on integration were on Oracle AQ (from Oracle 8i onwards) and Oracle Workflow Standalone 2.6.x. One of my big dissapointments were that from Oracle 10g AS and DB, Oracle Workflow is desupported. You may use it as long as the 10g licenses last (either for database or AS). But then it is End of Life time. In 11g Oracle Workflow is not delivered anymore. And that is quite a pity, since it was quite a good Workflow tool, especially for datasbase centric applications. The nice thing is that it recides and runs completely in the database. Also it has a pretty good Eventing system. You can define events on a web-based UI. Then you can subscribe Oracle Workflow processes, Java-classes or pl/sql rule-functions on an event.

The good thing about business events is that you can make applications really independend from each other. If you have an application and you need that something happens if a certain mutation in your application is done, for example on creation of an order, the order should be interfaced to another application or logged, then you would create a function call in a database trigger, or from your ADF-BC entity-object. The side-affect is that if your called-functionality is invalid, not available or gives errors, then your calling-application would not work. Also if it is a long running process that is called, than it will all happen in the session of the end-user, who must wait until all processing is done.

And that all is wrong. The calling application should only notify an infrastructure component (like a business event system) and should not care about what is done with this notification. Doing so the end-user get's his sesson back immediately. The application won't disfunction because of corrupted subscribing applications. The application has done what it's responsible for.

The business event system then will handle all the subscriptions in the back-ground. And handle all errors, throw them to an error hospital for example.

But unfortunately the Oracle Workflow Business Event System is no more. Although it is still available in Oracle's E-Business Suite. But that is quite large to install for only BES. EBS is leaning heavily on BES, since most mutations on EBS entities have Events defined on which as an EBS-developer you can subscribe your custom-code. Even if there is no Event available for what you need to do, you shouldn't code it directly on a trigger, but let a database trigger raise your newly defined custom event. And subscribe your code on that event.

This week I delivered the OPN Bootcamp on SoaSuite11g. It was very nice and I'm looking forward to the next to come (9-11th february in Belgium). There's already written a lot on SoaSuite 11g, but little on the comeback of BES: the Event Delivery Network.
Although I like the SCA/Composite. Although I like the integration of all components. But if I must choose, I would pronounce the EDN as the most interesting and promissing addition.

In the OPN Bootcamp there is a small chapter with a little lab to smell just a little on EDN. See also the chapter in "Getting Started with SoaSuite11g". A larger explanation is found here. And here you can read about the managing of EDN.

Raising/publishing an event from a mediator is really easy. Just a few clicks. Also subscribing on a event is just a few clicks away. Also you can raise/publish events from ADF-BC or from Java. But what I miss is an explanation on how to raise an event from Pl/Sql. It is mentioned that it is possible. And in the managing-guide, you'll see how you can create database-agents. But I have to find out how to do it from Pl/Sql. I'll post it when I've found out the how-to.

I've great expectations on EDN. I hope and expect that Oracle will expand the useability further more with enabling other technologies to be able to publish and subscribe to events, the propagation of events to remote systems over WANs, etc. I think they'll do because they need a good Event System when for example BPEL and/or BPM is to replace Oracle Workflow in E-Business Suite.

But we as SOA/EDA consultants have to train Business Analists to think in events. EDN is targetted to the Business. So they have to embrace the concept of events.

Wednesday, 6 January 2010

Failed to compile the generated BPEL classes

Today I ran in a weird problem, one moment I could compile my bpel process, a minor change later I couldn't. I got the very descriptive error (not):
Error: Failed to compile classes. Failed to compile the generated BPEL classes for %process-name%.

After a little trial and error I ran a expression in a while loop:
condition="bpws:getVariableData('EKDSuccess')=&quot;false&quot; and bpws:getVariableData('EKDTryCount')<=bpws:getVariableData('EKDMaxRetries')" 


It turned out to be the quotes around the word "false". I changed them to single-quotes, like:

condition="bpws:getVariableData('EKDSuccess')='false' and bpws:getVariableData('EKDTryCount')<=bpws:getVariableData('EKDMaxRetries')"


And then it compiled. It was in 10.1.2 (old I know). So maybe in 10.1.3.x it is solved.

Tuesday, 5 January 2010

Passing parameters to an XSLT in BPEL

Yesterday for me it became handy to be able to pass parameters to an XSLT in BPEL. I've seen the need earlier, but solved it a different way. By setting the target element with a default and then over writing that default in a later assign-copy step.

But yesterday I had to transform a document a variable number of times, for a list of elements. In my case I had a document with a list of recipients and I had to transform that to another document for each recipient. Each recipient had a number of elements with personal and address information that had to be transformed to the target. So simply defaulting and overwriting would not work, or was a lot of work.

To pass parameters as arguments to a XSLT is quite easy and neatly described in several blogs, amongst others in Sudheer Dhurjati's Blog.

In my case I had to pass an index to be able to select the particular recipient that I need to transform. And another parameter that holds a number of documents that is determined from another source:
<?xml version="1.0" encoding="UTF-8" ?>
<parameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.oracle.com/service/bpel/common /S:/DEV/Sources/BPEL/Processes/CRMI_COM_VersturenBerichtContactMgt/1.0/src/xsltparameters.xsd"
xmlns="http://schemas.oracle.com/service/bpel/common">
<item>
<name>AantalBijlagen</name>
<value>2</value>
</item>
<item>
<name>OntvangerIndex</name>
<value>1</value>
</item>
</parameters>

In Sudheer's blog (and in other examples) the parameters are initialized by copying an XML fragment with the particular parameters. If you have to change one of the parameters this could be done by doing an indexed xpath-expression in the <to> of the assign step:
[/prm:parameters/prm:item[prm:name='OntvangerIndex']/prm:value]
with [http://schemas.oracle.com/service/bpel/common] as [prm]
<value xmlns="http://schemas.oracle.com/service/bpel/common">1</value>

But for flexibilities sake, I would propose a slightly different approach. And for that I need an adapted version of the XSD:
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bplcmn="http://schemas.oracle.com/service/bpel/common"
xmlns="http://schemas.oracle.com/service/bpel/common"
targetNamespace="http://schemas.oracle.com/service/bpel/common"
elementFormDefault="qualified">
<xsd:element name="parameters">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="bplcmn:item" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="item" type="bplcmn:itemType"/>
<xsd:complexType name="itemType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

In this XSD I created 'item' as a seperate element based on a seperated (named) complex-type. I'm actually not so fond of nested complex-types, because it prevents you from using elements lower in the hierarchy for seperate variables.
Using this XSD, you can create a seperate item-variable, based on the item-element.
This is item-variable can be filled with a name and value, just by copying to the particular elements.
The item variable has then to be added to the parameters node using the addChildNode function:
ora:addChildNode(bpws:getVariableData('XsltParams','/bplcmn:parameters'),bpws:getVariableData('item','/bplcmn:item'))
The first argument of the addChildNode function denotes the element under which you want to add a child. In the expression above this is the 'parameters' element in the XsltParams variable. The second argument is the node you want to add as a child, in this case the 'item'-variable. Of course this expression is the <from>-expression in the copy-rule of the assignment step. The <to> will be most of the times the parent element used in the addChildNode expression:
<to variable="XsltParams" query="/bplcmn:parameters"/>
I found the description of the addChildNode function in the expression builder not so clear. So this might also be helpfull for other situations where you have to build up a node structure dynamically.