In this example we'll build a distribution process that will act as a typical Internet mailing list.
A typical (very simple) mailing list gets one or more email messages and redistributes them to all the members of that list. The result is that each member receives all the messages sent to the list.
In the example, the messages are retrieved from a typical POP3 account.
The aforesaid process can be summarized in the following steps:
The configuration is what goes in between inside the mandatory <distribution></distribution> tags.
<distribution> <functions prefix="d" class="org.cid.distribution.plugins.common.CommonFunctions" /> <functions prefix="mail" class="org.cid.distribution.plugins.mail.MailFunctions" />
We start declaring a couple of function libraries we will be using later.
<name>simplelist</name> <description>List for testing</description> <start>start</start>
Following, we define the typical properties that every configuration needs: the process name and an optional description.
We also specify the name of the starting node. This will be the node which will receive the first message (the start message).
Next we declare some user properties. As we will be accessing mail servers, is useful to group that information on the top of the file like this:
<property name="smtp.host" value="10.0.0.2" /> <property name="pop.host" value="10.0.0.2" /> <property name="pop.username" value="usuariotest" /> <property name="pop.password" value="test1ngus" /> <property name="list.address" value="usuariotest@localhost" /> <property name="list.prefix" value="[${ process.name }] " />
We need to get the member list from somewhere. To manage mailing lists and members we will be using the list plugin package, which provides many components to retrieve member lists.
This time, we will be use a StaticMemebrManagerComponent, which is a component that allows us to define the list of members in the configuration file:
<component id="members" class="org.cid.distribution.plugins.list.StaticMemberManagerComponent"> <members> <member><name>Ana Álvarez</name><address>testuser1@localhost</address></member> <member><name>Bea Bermúdez</name><address>testuser2@localhost</address></member> <member><name>Carmen Jones</name><address>testuser3@localhost</address></member> </members> </component>
Components do not take part in the message processing as a node, but behave as services avaliable to nodes. We will use this component later, in the distribute node.
Now we continue defining the process flow. Process flow is defined by nodes, which perform actions on messages and also return messages.
<node id="start"> <forwards> <forward nodeId="pop3" /> </forwards> </node>
The start node is composed uniquely of a forwards section. Only one forward is defined, which will make all the messages go to the pop3 node. As this is the starting node, only one message (the start message) will be received by this node.
<node id="pop3"> <handler class="org.cid.distribution.plugins.mail.POP3Handler"> <protocol>pop3</protocol> <host>${ properties['pop.host'] }</host> <username>${ properties['pop.username'] }</username> <password>${ properties['pop.password'] }</password> </handler> <forwards> <forward nodeId="edit" /> </forwards> </node>
This node is activated by the start message received from the start node. It contains a POP3 handler from the mail plugins package.
The POP3Handler ignores the start message (it is not useful anymore), and connects to the POP3 server defined in the handler. We are using the properties defined before using Expression Language.
The message or messages retrieved from the POP3 server are then forwarded to the edit node defined in the <forwards> section.
<node id="edit"> <handler class="org.cid.distribution.plugins.mail.MailEditorHandler"> <edit> <action>setSubject</action> <data>${ mail:prefixSubject(properties['list.prefix'], message.subject) }</data> </edit> </handler> <forwards> <forward nodeId="backup" condition="${ true }" /> </forwards> </node>
This node contains a MailEditorHandler. A MailEditorHandler edits the message in different ways: altering its subject, recipient or sender, adding a footer...
In this case, we are setting the message subject. We try to prefix it with the list name, so a message with a subject like "The photograph" will end up with a subject like "[listname] The photograph". This is very common, as almost every mailing list in the Internet does this with the message subject to allow the people to easily distinguish that messages from the rest of their mail.
We use a function inside an expression to calculate the new subject. The mail:prefixSubject function joins two strings together, and so it takes two parameters.
The first string is properties['list.prefix'], which is a property defined at the top of the file that will be storing the value "[simplelist] ". The second string is the variable message.subject, which contains the subject of the message being processed. The prefixSubject function concatenates both strings, and does it handling special cases (like when the message already has that prefix, and maybe the prefix 'Re:' or 'Forward:' too).
After that the message is sent to the backup node, as defined in the forwards section.
<node id="backup"> <handler class="org.cid.distribution.plugins.mail.MailWriterHandler"> <url>history/${ process.name }/${ d:date('yyyy-MM') }/${ process.name }-${ d:date('yyyyMMddHHmmss') }-%U.mail</url> <append>false</append> <allowOverwrite>false</allowOverwrite> </handler> <forwards > <forward nodeId="distribute" condition="${ true }" /> </forwards> </node>
This node contains a MailMessageWriterHandler. This handler writes a mail message to a file, for backup purposes or for later processing (like generating list statistics).
The url element defines the location where the files will be saved. This handler accepts many types of urls, we could in example store the files in a ftp server or a remote shared folder.
This time, we use expressions to include the process name and a timestamp in the path. We also use the special sequence %U, which generates an unique filename for the file. Remember to use it because you must guarantee that the filename will be unique, unless you set the append and the allowOverwrite properties to true.
The next step consists in replicating the message for every list member:
<node id="distribute"> <handler class="org.cid.distribution.plugins.list.ListCombinatorHandler"> <memberManagerId>members</memberManagerId> <listAddress>${ properties['list.address'] }</listAddress> </handler> <forwards> <forward nodeId="send" /> </forwards> </node>
The DistributionListHandler makes a copy of the message for every member in the list. It is a handler from the list plugins package, and knows how to "talk" with the member list component that we defined at the beggining.
We give the DistributionListHandler handler a reference to the StaticManagerComponent component. When the first receives a message, it replicates it for each member in the list.
We can also provide a listAddress and the handler will set it as the reply-to address of each mail.
Note that this node returns more message than it receives. Each one is forwarded to the send node for delivery.
Now we send each message to its recipient. The node is quite self-explanatory:
<node id="send"> <handler id="mail" class="org.cid.distribution.plugins.mail.SMTPHandler"> <host>${ properties['smtp.host'] }</host> <closeAfterMessages>10</closeAfterMessages> </handler> <forwards /> </node> </distribution>
We close the smtp connection after every 10 messages sent, because some mail servers don't allow more messages in a single connection. Be aware that each mail server has its own rules and restrictions. If you don't define this property, the SMTPHandler will close the connection after each message, which will be slower.
And that's all. We've completed the definition of the process.
The "simple list" example is is included in the distribution package. Also it can be accessed online: example-simple-list.xml.