Call M3 API from Event Analytics rules

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

That’s it. Let me know what you think in the comments below.

Published by

thibaudatwork

ex- M3 Technical Consultant

20 thoughts on “Call M3 API from Event Analytics rules”

  1. Very cool!

    Did you ever consider connecting to the APIs using REST? Or do you think that might be too much overhead?

    Like

    1. 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.

      Like

  2. 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

    Like

      1. 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 🙂

        Like

  3. 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

    Like

    1. 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.

      Like

  4. 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;
    }

                }
                if (OneChange==1){
                     event.postEvent("EvtItem");
                }
    

    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

    Like

    1. 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.

      Like

  5. 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

    Like

    1. 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

      Like

    1. 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.

      Like

  6. 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

    Like

Leave a comment