/ Development  

The OpenText best-practice implementation of "receiveEmail"

Hi there “Process Automation” fans,

Welcome to a new installment of “Process Automation” tips.

Last week we put the service call receiveEmail to the test. For this week we’ll implement the undocumented best-practices of OpenText as they see “receiveEmail” as a middleware call which you don’t put at your front-desk (which is for me for unclear!?). Ok, we just swallow this idea with a recommended implementation…Watch and learn how we create a best-practice on top of a best-practice!


Let’s get right into it…

We split this post into three sections:

  1. Sending out a mail from an entity (the easy part)
  2. Reply to the same mail and saving it within the entity instance (where we expose <{ItemId}@opentext.com> in a reference header!)
  3. Creating an entity instance from the mail AND saving the mail with the instance

But first…We require a mail server to communicate with AND we require an entity with mail-enriched building blocks.


Sending out mail from an entity

That’s as easy as following the first section “Send out an E-mail” of my own post “Master the game; Using Gmail as the ultimate email service container for our beloved platform”. Once in place, you can send a mail via SendMail (after doing the SetProfile call!).

You see me sending a mail to antal@bos-ictservices.nl with a from-address OPA@gmail.com. For now, that’s fine, but later, you want to reply to the from-address! So, eventually this must be a valid mail-address to send a mail to! It should even be a from-address that we can read out with a mail configuration in the ‘Email’ service container (not for now!).

Next, we create a new ‘Case’ entity and add the ‘Email’ building block with new configuration:

receive_mail_001

Notes:

  • Save the mc_case config into a configs folder of your project (just a good habit!)
  • You need to add the ‘Content’ BB to save mails (as configured in the ‘Email’ BB)
  • In the lyt_default, add the ‘Emails’ panel to view it all in runtime!
  • Don’t forget to also add the ‘Web service’ BB with the CRUD operation (for now)
  • With this ‘Web service’ BB, you also require a service container of the ‘Application server’ in the ‘System Resource Manager’ artifact!

Publish it all into runtime, create a new ‘Case’ instance, and create/send a new mail:

receive_mail_002

It will use the SetProfile details to (again) send a second mail (first one was from last week!).

Now for the great <{ItemId}@opentext.com> trick!?…Save the mail (so, you get a .eml file) from you client. I use Gmail itself (for the demo) where you can download the file here:

receive_mail_003

Open it with Notepad++ and watch this:

receive_mail_004

AHA-Moment…AND BUSTED!! 🤓

Time for our next section where exactly this reply reference is important!


Reply to mail and save with entity

So, now the other way around! I have my mail in the inbox of antal@bos-ictservices.nl with a reference in the header to my entity ID! How and where do I reply to? Well, you need to reply (in our demo) to OPA@gmail.com, and our mail configuration (of the mail service container) needs a “poller” to read the inbox of OPA@gmail.com with a trigger on what to do when it finds a new mail!

For this case we can perfectly (for now!) use the documentation example as emailbox configuration. In this example, you find (again!) the receiveEmail service call which simply maps the email data to the correspondent service call entries (‘from’, ‘subject’, and ‘body’ as Base64 String…incl. possible mail-attachments as multipart request!)

Now, double-check two things:

  1. Did you create a label/folder OPA_REPLY in the (for me OPA@gmail.com) mailbox?
  2. Did you create a rule to apply this label when @opentext.com is in the header? Probably not, as I see that’s a limitation in Gmail! #WTF…So, we need to improvise with a different “reply” reference which is the subject has Re:!

receive_mail_005

After this change (including reading the comments in the XML part!) in the ‘Email’ service container, do a restart, check you logging Application_Server.xml and 🤞 it gets green again (or solve the problem from your logging feedback)!

I have white smoke, so time for a reply-mail test (from antal@bos-ictservices.nl to OPA@gmail.com)…This is the result:

receive_mail_006

Nicely saved in the inbox of the referenced entity instance! 😎

Do you wonder what happens when we just send a new mail (without @opentext.com in the references!) that has a subject part of Re:? Yes, please…And guess what…In the email subject 'Re: test...', item ID is missing between [# and ]! AHA-moment…That’s a familiar one from last week!

Be careful on this last assessment as it looks to me like the mail container gets into some endless looping!? Even a restart does not work as there is already a table entry in the database which starts the looping again…What table? Well, what about the email table with an entry processing_status='RELEASED'! Setting the value to COMPLETED solved it nicely!

I’m also cheating a little here because I don’t have the header-rule in place which will solve it. But still, you must avoid endless looping at all cause!

This last comment section does ring a completely different bell! Now I understand why OpenText’s advice (because of middleware!?) is against using receiveEmail directly in the mail configuration of the relevant service container. Probably, because of this loose cannonball behavior on wrong receiving mail (I agree…the chance isn’t high, but it’s there)! Moving this logic to a BPM makes this (in my opinion #BUG!) controllable with an aborted BPM as a result…Please tell me I’m incorrect here, but I’m pretty certain that’s the exact reason (which they don’t communicate obviously)! 🤠

Time to move on…Time to remove the middleware call from the mail configuration and moving it to a solid (monkey-proof?) BPM implementation!


Create entity from mail and save the mail with the instance

Ok…When we remove the middleware call from the mailbox configuration, we need to replace it with something else? Yes, that (my friends) will be a BPM call AND executing a BPM in this (webservice only!) perspective can happen in two ways:

  1. Calling ExecuteProcess with the proper input; Read about it here, but you can also search to find more examples on the topic!
  2. Extracting a service out of a BPM and calling that service with the proper input; Read about it here

We go for the ExecuteProcess approach as you don’t need a dedicated BPM service container AND we don’t need a dedicated service call to expose to others that require it.

So, we need a simple BPM (bpm_manage_mail) first (just an easy one-activity BPM…we do the implementation later):

receive_mail_007

Now we update our mail config by replacing receiveEmail with ExecuteProcess…And some other magic things:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<message>
<method>ExecuteProcess</method>
<timeout>60000</timeout>
<namespace>http://schemas.cordys.com/bpm/execution/1.0</namespace>
<!--Use a correct DN from the 'User Manager' artifact executing this method!-->
<user>cn=sysadmin,cn=organizational users,o=opa_tips,cn=cordys,cn=defaultInst,o=25.1.com</user>
<!--Always use TRUE...don't shoot the messenger-->
<sync>true</sync>
<input>
<type>definition</type>
<receiver>nl-bos-generic/bpms/bpm_manage_mail</receiver>
<source>Run from Mail trigger</source>
</input>
</message>

Preview the end-result (with details from below) here

Make sure to restart the service container after this change, and do a first test sending out a mail from the entity, and reply to it like in the previous section (re: in the subject). In the PIM, you should see an instance of our BPM:

receive_mail_008

Continue if you have a green light…

Now we need a BPM implementation, AND our BPM must be so smart that when we get a reply to a mail, we need to find the entity and the mail needs to save with the entity (via our well-known receiveEmail service call)! When we don’t find an entity instance (we assume it’s a new instance) it needs to create a new entity instance, AND in the new entity we also need to save the mail!

Make sure to understand the scope as there is one glitch in this story…That’s when users start to manipulate the subject on a reply mail! For us no big deal, as manipulation will route to a new case; That’s a choice the end-user makes not following procedures!

This the pseudocode for our BPM implementation (which always brings new insights with a ChatGPT collaboration…try it!):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void processEmail(String subject, String to, String from, String attachment) {
String caseNr = extractCaseNumber(subject);
Case case = null;

if (caseNr is not null and not empty) {
case = findCaseInstanceByCaseNr(caseNr);
}

if (case is null) {
case = createNewCase();
}

receiveEmail(case.ItemId, subject, to, from, attachment);
}

Now for the implementation part…Open the one-activity BPM from the beginning of this section, and start with an input message containing a set of elements in the BPM messagemap:

receive_mail_009

AND set this input message in the green ‘Start’ construct of the BPM:

receive_mail_010

Next step is to map our input data from the mail configuration into our BPM (check the <message> and mappings parts):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<message>
<method>ExecuteProcess</method>
<timeout>60000</timeout>
<namespace>http://schemas.cordys.com/bpm/execution/1.0</namespace>
<!--Use a correct DN from the 'User Manager' artifact executing this method!-->
<user>cn=sysadmin,cn=organizational users,o=opa_tips,cn=cordys,cn=defaultInst,o=25.1.com</user>
<!--Always use TRUE...don't shoot the messenger-->
<sync>true</sync>
<input>
<type>definition</type>
<receiver>nl-bos-generic/bpms/bpm_manage_mail</receiver>
<source>Run from Mail trigger</source>
<message>
<!--Use a matching namespace here equal the one in the BPM properties for this message!-->
<bpm:mail_input xmlns:bpm="http://schemas.cordys.com/default">
<bpm:subject/>
<bpm:to/>
<bpm:from/>
<bpm:attachment/>
</bpm:mail_input>
</message>
</input>
<mappings>
<mapping>
<source>.//subject</source>
<value src="Subject" />
</mapping>
<mapping>
<source>.//to</source>
<value src="To" />
</mapping>
<mapping>
<source>.//from</source>
<value src="From" />
</mapping>
<!--This is the full mail itself-->
<mapping>
<source>.//attachment</source>
<value src="Base64Attachment" />
</mapping>
<!--Possible mappings we saw passing by in the logging of our failures (not for this post)!
<mapping>
<source>.//sysStorageID</source>
<value src="sysStorageID" />
</mapping>
<mapping>
<source>.//sysLogicalID</source>
<value src="sysLogicalID" />
</mapping>
<mapping>
<source>.//sysRawEmail</source>
<value src="sysRawEmail" />
</mapping>
-->
</mappings>
</message>

See the end-result here including the “secret” comments!

Restart the service again, and evaluate your mail reply action…Now watch the PIM, and check the input message for the instance:

receive_mail_011

OHHHH YEAH 😎…It’s a party! 🥳

With this in place, we continue the craftsmanship of our BPM from our pseudocode…Start with this:

receive_mail_012

From the messagemap, you can add the ‘local’ variable/element caseNr! (like you did the input message; now it’s not a message, but just an element on itself!)

NOW JUST WAIT A MINUTE…(I’m processing here!…)

Why on earth do we want to check the case number from a reply mail if the ‘Base64Attachment’ string in our mail has the ‘Reference’ <{ItemId}@opentext.com>…remember? Why don’t we decode the String in the XPath call com.eibus.util.Base64.decode("{your_Base64String}") (see here), and grab the entity ‘ItemId’ from it? That’s a far better/solid solution with a fail-save!

Just a moment of “AHA”! Is this possible from XPath?…Well, watch this:

1
2
3
4
5
substring-before(
substring-after(
com.eibus.util.Base64.decode(concat(bpm:mail_input/bpm:attachment/text())), 'References: <'
), '@opentext.com>'
)

Notes:

  • The before/after substring will grab a string somewhere between two specific values: References: < and @opentext.com>
  • The extra concat() makes sure the com.eibus.util.Base64.decode() accepts the input as a “String” value…don’t shoot the messenger!

What will it do with our pseudocode? It will be more like this:

1
2
3
4
5
6
7
8
9
10
11
12
public void processEmail(String subject, String to, String from, String attachment) {
String caseItemId = extractCaseItemId(attachment);
Case case;

try {
case = readCase(caseItemId);
} catch (Exception e) {
case = createCase();
}

receiveEmail(case.ItemId, subject, to, from, attachment);
}

Our BPM can now execute like this:

receive_mail_013

Notes:

  • You can add webservices from the context menu of the relevant activities
  • The ReceiveEmail requires a runtimereference in your project of Method Set Entity Email in category OpenText Entity Runtime! No clue? Read about it here
  • I convert my ‘local’ variable/element caseNr to caseItemId.

Have a look at this consolidated view of the messagemap for this BPM (read this always from right/source to left/target!):

receive_mail_014

Publish your changes, and give it a shot on a reply mail from your entity instance AND a new mail for create a new case. Check both instances on the inbox items for a validation on a correct (and solid!) implementation!

In my VM, it’s white smoke with green checkmarks all over the place (on both test-cases)! 😎

receive_mail_015

For the new case test, you still need to make sure you match the mail rule which is starting with Re:, but you can add an extra rule in your mailbox that will move the mail to the same label/folder OPA_REPLY which will run the same BPM! Now that I’m ReThinking this over, it would be better to use a label/folder like OPA_MAIL!

Just an interesting question? Can we also create a new entity instance from a mail send out by another entity applying the subject of our mail-rule? Well, not anymore with our solid “Reference” check! If we had checked the ‘caseNr’ from the subject, the answer is YES when you manipulate the subject on your reply!

Another question? What happens to mail that does not apply our mail rule? Well, what do you think will happen with those mails? Indeed, they will stick in your mailbox not triggering any BPM via the mail service container! 😉


Again, a best-practice (on top of a best-practice!) “DONE” with a solid BPM implementation for correct receiving emails from a server. Why the initial best-practice is undocumented, is unclear for me?? It raises other questions on what other best-practices are available from the OpenText team? Tell us! We can all learn from it which makes the community stronger with solid best-practice implementations at your licensed customers! No more to say; Let’s see…I go for a weekend and see you all next time in another topic about “OpenText Process Automation Tips”.

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The Process Automation guy”?