Here is how to call M3 API from a Drools rule in Infor Event Analytics; this is a common requirement.
Sample scenario
Here is my sample business case.
When a user changes the status of an approval line in OIS115 (OOAPRO), I have to find the order type (ORTP) of the order to determine which approval flow to trigger in Infor Process Automation (IPA), but ORTP is not part of the table OOAPRO, for that reason I must previously make a call to OIS100MI.GetHead.
I could call M3 in the approval flow, but false positives would generate noise in the WorkUnits.
Is it possible?
I asked Nichlas Karlsson, Senior Architect – Business Integration at Infor, if it was possible to call M3 API directly in the Drools rule. He is one of the original developers of Event Hub and Event Analytics and very helpful with my projects (thank you) although he does not work with these products any longer. He responded that Event Analytics is a generic software with no specific connection to M3, so unfortunately this is not possible out of the box, however it is a common requirement. He said I could solve it using MvxSockJ to call M3 APIs in my own Java class, included in a jar that I put in the lib folder. He added to not forget that the execution time for all rules within a session must be less than the proxy timeout, i.e. 30s. And I would also need to manage host, port, user, password and other properties in some way.
Instead of MvxSockJ I will use the MI-WS proxy of the Grid as illustrated in my previous post.
Sample Drools rule
Here is my sample Drools rule that works:
package com.lawson.eventhub.analytics.drools; import java.util.List; import com.lawson.eventhub.analytics.drools.model.Event; import com.lawson.eventhub.analytics.drools.model.HubEvent; import com.lawson.eventhub.EventOperation; import com.lawson.grid.node.Node; import com.lawson.grid.proxy.access.SessionId; import com.lawson.grid.proxy.access.SessionProvider; import com.lawson.grid.proxy.access.SessionUtils; import com.lawson.grid.proxy.ProxyClient; import com.lawson.grid.registry.Registry; import com.lawson.miws.api.data.MIParameters; import com.lawson.miws.api.data.MIRecord; import com.lawson.miws.api.data.MIResult; import com.lawson.miws.api.data.NameValue; import com.lawson.miws.proxy.MIAccessProxy; declare HubEvent @typesafe(false) end rule "TestSubscription" @subscription(M3:OOAPRO:U) then end rule "TestRule" no-loop when event: HubEvent(publisher == "M3", documentName == "OOAPRO", operation == EventOperation.UPDATE, elementOldValues["STAT"] == 10, elementValues["STAT"] == "20") then // connect to MI-WS Registry registry = Node.getRegistry(); SessionUtils su = SessionUtils.getInstance(registry); SessionProvider sp = su.getProvider(SessionProvider.TYPE_USER_PASSWORD); SessionId sid = sp.logon("Thibaud", "******".toCharArray()); MIAccessProxy proxy = (MIAccessProxy)registry.getProxy(MIAccessProxy.class); ProxyClient.setSessionId(proxy, sid); // prepare input parameters MIParameters p = new MIParameters(); p.setProgram("OIS100MI"); p.setTransaction("GetHead"); MIRecord r = new MIRecord(); r.add("CONO", event.getElementValue("CONO")); r.add("ORNO", event.getElementValue("ORNO")); p.setParameters(r); // execute and get output MIResult s = proxy.execute(p); List<MIRecord> records = s.getResult(); // all records if (records.isEmpty()) return; MIRecord record = records.get(0); // zeroth record List<NameValue> nameValues = record.getValues(); // all output parameters String ORTP = nameValues.get(4).getValue(); // PROBLEM: somehow nameValues.indexOf("X") returns -1 // make decision if (ORTP.equals("100")) event.postEvent("ApprovalFlowA"); if (ORTP.equals("200")) event.postEvent("ApprovalFlowB"); if (ORTP.equals("300")) event.postEvent("ApprovalFlowC"); end
Note: You will need to drop foundation-client-10.1.1.3.0.jar in the lib folder of Event Analytics, and restart the application
Limitations
There are some limitations with this code:
- The execution time must be less than the 30s proxy timeout
- Limit the number of return columns; there is currently a bug with Serializable in ColumnList, see Infor Xtreme incident 8629267
- If the M3 API returns an error message it will throw the bug with Serializable in MITransactionException, see Infor Xtreme incident 8629267
- Somehow NameValue.indexOf(name) always returned -1 during my tests, it is probably a bug in the class, so I had to hard-code the index value of the output field (yikes)
- I do not know how to avoid the logon to M3 with user and password to get a SessionId; I wish there was a generic SYSTEM account that Event Analytics could use
- For simplicity of illustration I did not verify all the null pointers; you should do the proper verifications
- The code may throw MITransactionException, ProxyException and IndexOutOfBoundsException
- You can move the Java code to a separate class in the lib folder; for that refer to my previous post
Related articles
- How to call M3 API from the Grid application proxy
- Application proxies in the Infor Grid
- Java code in Event Analytics rules
That’s it. Let me know what you think in the comments below.
Very cool!
Did you ever consider connecting to the APIs using REST? Or do you think that might be too much overhead?
LikeLike
Hallo Janne. Thank you. Yes, I thought about using REST but then like MvxSockJ you would need to store/retrieve the host/port and establish socket connections which the proxy abstracts and provides a connection pool.
LikeLike
From where I can get foundation-client-10.1.1.3.0.jar file..?
LikeLike
Good question. Follow the link to my previous post, there is a screenshot to the path: https://thibaudatwork.wordpress.com/2015/05/12/how-to-call-m3-api-from-the-grid-application-proxy/
LikeLike
Hi,
Very helpful post!!!
I have a question about how to run SQL statement through MAK. Not sure if this blog already have any post about it.
I am trying to run a select sql through MAK (MMS200MI – New transaction added GetITMMST(), want to run “Select count(*) from MITMAS where MMSTAT = ’50’ (Or any statys))
=============================================================
//SQL
String W2TempString = “Select count(*) from lib/MITMAS where mmstat = ’50’”;
int W2TempCount;
Statement W0TempStatement = null;
ResultSet rs = null;
try {
rs = W0TempStatement.executeQuery(W2TempString); //===> throws error here
while (rs.next()) {
String column1 = rs.getString(“MMCONO”);
String column2 = rs.getString(“MMITNO”);
}
} catch (SQLException e) {
e.printStackTrace();
// TODO Auto-generated catch block
MSGID.move(“SQL query wrong”); // Mandatory fields are missing
IN60 = true;
return;
}
It throws error @executeQuery. I think I need to do connection, but don’t know how to make dynamic connection. Trying something like below.
String url = “??depend on environment??”;
String user = “LDAZZ or ZD.USR or session.usr”;
String password = “???not sure??”;
Connection connection = null;
try { // Load class into memory
// Establish connection
connection = DriverManager.getConnection(url, user, password);
Could you please help here? Am trying to check all variables to get the url/usrid/pwd.
Thanks in advance,
Veena
LikeLike
Hi Veena. Thank you. I do not work with MAK (I stay away from it). My personal understanding is that you do not simply do SQL/JDBC from an M3 program as M3 has better built-in abstractions for it; but I am no MAK person. Ask your question in one of the forums; see the Links page for the list (try Potato IT forums, Infor Guru, IT Toolbox), https://thibaudatwork.wordpress.com/links/ . /Thibaud
LikeLike
Thanks a lot for quick reply!!!
M3 does have built-in features to read/update/delete data sequentially. However, I couldn’t anything which will return me count. I already put my question on potatoit, I will definately post it on infor guru and it toolbox.
Thanks again 🙂
LikeLike
UPDATE: It should be possible to authenticate with a certificate signed by the grid root key of the keystore server.ks. To be tested.
LikeLike
Hi,
Thanks for this article.
Is it possible to browse all getElementValue (“xxx”) after the “then”. In fact, what I want to do is trigger the API only if one of the elementValue is different from oldElementValue. Do you have an idea.
Best regards and Thanks
LikeLike
Yes you can, it’s a Java data structure like any other. For example elementValues[“x”] != elementOldValues[“x”]. Check the data structure’s type in the imports, introspect it, and use its methods. All standard Java. Also, check the JBoss Drools documentation.
LikeLike
By looking at the imports, the elementValues data structure is of type com.lawson.eventhub.ElementData, so explore that.
LikeLike
Thank for your reply.
I have the following import
import com.lawson.eventhub.ElementData;
and code :
int OneChange =0;
for (ElementData element : event.getElements()) {
if (element.getValue() != element.getOldValue()) {
OneChange =1;
}
and I have the following message:
Rule Compilation error The method getElements() is undefined for the type HubEvent
Help Help, my java knowledge is terrible.
Best regards and Thanks
LikeLike
Open the Event Hub Jar files (eventhub*.jar from the Infor Product Download > Infor Grid Extensions, or from the Infor Grid folder on your server) into a JAR explorer (e.g. JD-GUI), mostly eventhub-common.jar, and explore package com.lawson.eventhub. The class EventData has a method java.util.ArrayList<ElementData> getElements(). That’s your answer, ArrayList.
LikeLike
Hi,
I do not understand I put the following code:
package com.lawson.eventhub.analytics.drools;
import com.lawson.eventhub.analytics.drools.model.Event;
import com.lawson.eventhub.analytics.drools.model.HubEvent;
import com.lawson.eventhub.analytics.drools.model.Session;
import com.lawson.eventhub.analytics.drools.model.Start;
import com.lawson.eventhub.analytics.drools.model.Stop;
import com.lawson.eventhub.analytics.drools.model.Time;
import com.lawson.eventhub.analytics.drools.model.Util;
import com.lawson.eventhub.EventOperation;
import com.lawson.eventhub.EventPriority;
import com.lawson.eventhub.ElementData;
declare HubEvent
@typesafe(false)
end
rule “Subscriptions_OCUSMA”
@subscription1(M3:OCUSMA:CUD)
then
end
rule “OCUSMA_U_Customer”
no-loop
when
event: HubEvent(publisher == “M3”, documentName == “OCUSMA”, operation == EventOperation.CREATE || operation == EventOperation.UPDATE || operation == EventOperation.DELETE, elementValues[“CONO”] == “110” && elementValues[“startProgram”] == “CRS610”)
then
int OneChange =0;
for (List element : event.getElements()) {
if (element.getValue() != element.getOldValue()) {
OneChange =1;
}
}
if (OneChange>0) {
event.postEvent(“Customer”);
}
end
And I still have the message:
“Rule Compilation error ElementData can not be resolved to a type
The method getElements () is undefined for the type HubEvent”
Pouves you tell me what do not go I really do not understand.
A big thank-you
Best regards
LikeLike
In that case, one of the JAR files is missing or changed, or you have the wrong code. Go to the Event Analytics application properties page on the Infor Grid, and find the classpath with all the JAR files. Then go to the folders on the server and find those JAR files. Then use a disassembler like JD-GUI and open those JAR files. Then see what you have in package com.lawson.eventhub and introspect the classes. See if the event class has a method getElements and with what types. Then share the solution with us here. Hope it helps. –Thibaud
LikeLike
Is it possible to call M3 web service inside event hub?
LikeLike
Do you mean inside a Drools Rule in Event Analytics? Probably yes, it’s Java so technically you can do whatever. But I wouldn’t do it unless I knew the potential implications on the rest of Event Hub/Analytics or lack thereof (I don’t know either). Currently your official options are M3 Enterprise Collaborator (MEC), Infor Process Automation (IPA) and ION. Try faking a 60s timeout with java.Thread.sleep(60000) on a frequent event and see what happens.
LikeLike
Hi,
Is it possible to increase the timeout of EventAnalytics above 30 seconds? I’m currently trying to call out a webservice that is outside M3 and I’m getting a timeout error.
Thanks,
Suriya
LikeLike
You may have to increase the timeout in your web service client (your code), not in Event Analytics.
LikeLike