With the Lawson Grid, M3 introduced a new REST endpoint for calling M3 API where the response is returned in XML or in JSON.
Background
Historically, software clients that called M3 API needed to use proprietary libraries, such as MvxSockJ.class
for Java and MvxSockX_SVR.dll
for Microsoft languages. Over the years, Intentia introduced Movex Web Services to call M3 API using the SOAP standard. Then, Lawson introduced the Grid for cloud computing. In the process, Lawson replaced IBM WebSphere Application Server by open source software, it also replaced the Core Web Services for Lawson Smart Office, and it added Jersey, an open source implementation of JAX-RS (JSR 311). The positive outcome is that now, software clients can call M3 API using REST/JSON.
Why does it matter?
REST is increasingly popular, and years ago it started supplanting SOAP for web services [1].
Also, JSON is becoming the de facto data-interchange format, nicknamed “The Fat-Free Alternative to XML” [2].
Most importantly, it’s now possible to call M3 API without needing the proprietary libraries, and without needing Lawson Web Services. Software clients can now make simple HTTP Requests to call M3 API. This makes it easier to call M3 API from third-party software (such as Tibco, Business Objects, Web Methods, etc.) and from previously unsupported or difficultly supported programming languages (such as JScript.NET, JavaScript, Ruby, PHP, etc.).
What about Lawson Web Services?
The new REST endpoint for M3 API doesn’t preclude using Lawson Web Services. Lawson Web Services is still necessary for calling M3 API with the SOAP protocol, and for using the unavoidable M3 Display Program adapter which cannot be found in any other Lawson product. Also, Lawson Web Services is still necessary for Lawson Smart Data Tool.
REST endpoint
To access the new REST endpoint for M3 API, open a Grid Information page, which is accessible from any participating Host in the Grid:
For example: http://ussplu123:20005/grid/info.html
At the bottom of the page is the Application Name M3-API-WS, and it has a Link to the Rest Service‘s WADL:
For example: http://ussplu123:20005/m3api-rest/application.wadl
This WADL gives us the resource path:
execute/{program}/{transaction}
Where {program}
is the M3 API Program, like MNS150MI, CRS610MI, MMS200MI, etc. And where {transaction}
is that API’s transaction, like GetBasicData, AddAddress, LstByNumber, etc.
Let’s try to call the API CRS610MI.LstByNumber. The URL in my example would be: http://ussplu123:20005/m3api-rest/execute/CRS610MI/LstByNumber
The server will challenge us for HTTP Basic Authentication, we need to provide a valid M3 userid and password:
And the result in XML is a collection of MIRecord elements with Name/Value pairs:
JSON
We can get the result in JSON by adding the HTTP Header field:
Accept: application/json
The result is:
REST GUI
RESTClient is a great REST graphical user interface which we can use to test M3 API; you can download it at http://code.google.com/p/rest-client/
Paste the URL, set the M3 userid and password in the Authentication tab, optionally set the Header to get the response in JSON, and click Go!
Conclusion
With the Lawson Grid, software clients can now natively call M3 API from third-party software, and from previously unsupported or difficultly supported programming languages, without the need for proprietary libraries, and without the need for Lawson Web Services.
Updates
Input parameters
UPDATE 2012-07-17: You can set input parameters in the URL, for example: http://hostname:port/m3api-rest/execute/MMS200MI/GetItmBasic?CONO=531&ITNO=ABCDEF . Also, you can hook RESTClient to use Fiddler as a proxy in Tools >;; Options.
Max number of records
UPDATE 2012-07-31: You can set the maximum number of records returned with the matrix parameter maxrecs, for example: http://hostname:port/m3api-rest/execute/CRS610MI/LstByNumber;maxrecs=20?CONO=1
Output fields
UPDATE 2012-08-02: You can set the output fields returned with the parameter returncols, for example: http://hostname:port/m3api-rest/execute/CRS610MI/LstByNumber;returncols=CUNO,CUNM,CUA1,STAT,PHNO
I am using this blog as a base to call Movex REST Web Services using .NET. So far I am able to call GET methods but when it comes to calling POST transactions there is no documentation and I am really struggling to get it working. Anyone who has done it can you please post some sample code ?
LikeLike
Hi Virang. If you look at the WADL, the /execute/{program}/{transaction} only accepts the GET method, not POST, hence your struggle. Look at the WADL. There seems to be another resource executeRawPost that accepts POST; I don’t know what it does. Also, I suggest you try in RESTClient before adding a layer of complexity with .NET. /Thibaud
LikeLike
If you get POST working please leave some details.
LikeLike
UPDATE 2014-01-20: If you get the error HTTP 401 Unauthorized, try using the HTTPS port instead of HTTP.
LikeLike
UPDATE 2014-01-20: You can also get the URL from the M3-API-WS Test page at LifeCycle Manager > Grid > M3BE > Manage Application > Tools > M3-API-WS > Test > Show As REST.
LikeLike
When calling M3 REST APIs is Basic Authentication is the only authentication mechanism available ? When I try to execute M3 REST API from Soap UI and when the call fails it shows WWW Autorization as Negotiate, NTLM and Basic Realm=”TEST” in response header. Does that mean it supports NTLM and Basic authentication or just Basic Authentication as described in the article ?
LikeLike
Hi Virang. I think that’s configurable. For MWS SOAP Requests there’s a configurable authentication mechanism in the LifeCycle Manager. I can’t recall where.
LikeLike
Hi Thibaud,
I am trying to use Angular JS to display data to a page. When I access an m3 api via
http:// {server url}:20005/m3api-rest/execute/CRS610MI/LstByNumber;maxrecs=2?
I am able to get the data returned {with program, transaction, metadata and MIRecord array}, but I can’t extract the data’s specific contents:
Either by accessing the array
1{{row.CUNO}}
2{{row.CUNM}}
3{{row.CUA1}}
nor from the single header detail in the java script $scope.data.Program.
Would you have any idea here?
LikeLike
It seems M3 is not replying in json format.
This is what I get:
{“RowIndex”:”0″,”NameValue”:[{“Name”:”CONO”,”Value”:”100″},{“Name”:”DIVI”,”Value”:”TMW”},{“Name”:”LNCD”,”Value”:”GB”},
Not
{ “CONO”: “100”,
“DIVI”: “TMW” ,
“LNCD” : “GB”
LikeLike
Hi Jonathan, the M3 API REST Web Service does return valid JSON. It might not be an associative array you can conveniently access by key (I too wish it was), but it’s still a collection of objects you can easily traverse. Go to my other blog post on M3 API with Dojo Toolkit, and read my source code, I transform the response into an associative array with the JavaScript Array.map function: https://thibaudatwork.wordpress.com/2013/07/09/how-to-call-m3-api-using-the-dojo-toolkit/ . With it you can more conveniently do record[‘CUNO’]. /Thibaud
LikeLike
Jonathan. And if you have good results, please share them on this blog so others can benefit. I would really love to see a blog post here with AngularJS. I can create an author account for you. Shoot me an email. You’ll find my email address on my website at http://thibaudlopez.net/
LikeLike
Hi Jonathan,
Make sure you have same version of M3 Grid in your Development and Production environment. I developed one application with M3 REST API calls just to find out it failed in Production due to difference of Grid version and how it returns M3 API results in different JSON format. I called MMS200MI – GetUserData API in one environment it returned result as an array of values while in another one it is just one record.
I have to do something like below based on response
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode == HttpStatusCode.OK)
{
StreamReader readert = new StreamReader(response.GetResponseStream());
string x = readert.ReadToEnd();
JObject jObject = JObject.Parse(x);
//var def = new { Name = “”, Value = “” };
var def = new List();
if (x.Contains(“\”MIRecord\”:[“)) // Result as an array of objects
{
var nvp = JsonConvert.DeserializeAnonymousType(jObject[“MIRecord”][0][“NameValue”].ToString(), def);
Dictionary propVal = new Dictionary();
foreach (var sel in nvp)
{
propVal.Add(sel.Name, sel.Value.Trim());
}
//propVal.Add(nvp.Name,nvp.Value);
result = propVal;
}
else // Just one result
{
var nvp = JsonConvert.DeserializeAnonymousType(jObject[“MIRecord”][“NameValue”].ToString(), def);
Dictionary propVal = new Dictionary();
foreach (var sel in nvp)
{
propVal.Add(sel.Name, sel.Value.Trim());
}
//propVal.Add(nvp.Name,nvp.Value);
result = propVal;
}
}
}
LikeLike
Thanks for pointing me in the right direction!
Although I had to map the json data differently
$scope.allCustomers={};// customer 0-x…
var oneRecord = {}; //for each customer fields, field:value ex CUNO:100
//GetData
$http.get($scope.apiURL).success(function(data){
$scope.MIRecords = data.MIRecord;//remove metadata and headers
//Parse to a map
for (var i in $scope.MIRecords) {//get records
oneRecord={};
//get fields and values
for (var j in $scope.MIRecords[i].NameValue){
oneRecord[$scope.MIRecords[i].NameValue[j].Name] //field name
= $scope.MIRecords[i].NameValue[j].Value; //field value
}
$scope.allCustomers[i]=oneRecord;
}
LikeLike
Nice
LikeLike
UPDATE: We can force the CONO and DIVI by using lowercase cono and divi parameters in the URI path segment with semicolons. I found it here: go to the Infor ION Grid Management Pages > Node M3ApiWS > Module MI-WS > Global Management Pages (M3-API-WS) > Test; click Settings; set the Execution Settings: Company (cono), Division (divi), Max returned records, Date Format, and Run As User; then click Show As REST; it will show the full URL, e.g. /m3api-rest/execute/CRS610MI/Add;cono=702;divi=USA;maxrecs=100?CUTM=ACME&CUNM=Thibaud
LikeLike
I’m reading through this and wondering if anyone has tested using the Post methods? If so,can you provide any direction? Thanks!
LikeLike
Chris, read my #comment-1104 above, according to the WADL, method POST is not supported. I just tried again, and I still get HTTP/1.1 405 Method Not Allowed
LikeLike
Actually, I looked a bit more. According to the WADL you can POST JSON to /m3api-rest/execute. And according to the WSDL you can POST SOAP to /m3api/MIAccess?wsdl . I haven’t looked further. Use SoapUI to test. Anyway, why do you need POST, in other words why is GET not sufficient?
LikeLike
Thibaud,
Thanks for the response. We want to use REST web services to push and update data in M3. Is there another way we should look at doing this?
LikeLike
I do not understand. Does it not work with GET as explained in the blog post above? Inserts and updates in the REST style are supposed to happen only on POST, not GET, but I do not think the M3 API is that RESTful strict. I have not tried but it should work. Did you try?
LikeLike
Dude, I just tried, it works, the record got created, simple URL in browser (=GET method), https://host:21108/m3api-rest/execute/CRS610MI/Add?CUNO=THIBAUD2&CUTM=THIBAUD&CUNM=THIBAUD2&CUA1=123%20Main%20St
LikeLike
Thibaud,
Could you please show an example of a string passing the user’s credentials to call a REST API ? I was under the impression I can send the credentials as part of the string.
http://lsodev.xyz.com:33333/m3api-rest/execute/MMS200MI/GetItmBasic?CONO=1&ITNO=01000110002
Thank you,
Gaston
LikeLike
Hola Gaston. You cannot set the username:password in the URL, at least not anymore; in the past you could do it as per the URL specification, but since then the web evolved in terms of security restrictions, for instance URLs are saved in browser bookmarks and server logs, so it’s unsafe to set the username:password in the URL. Now you have to use it in the Authorization header. The example is in the Fiddler screenshot where it says “Authorization: Basic”. The value is the username:password, separated by a colon and Base64-encoded. The Infor Grid has other authentication mechanisms which I have not tried, I think NTLM/Kerberos or some other stuff like that.
LikeLike
Hola Thibaud,
In C#, I am using the HTTPWebRequest and abled to get the response value but when I tried to parse it using JOBject then I get an unhandled exception, will that be because it is not a valid JSon and what would you recommend to get the array of values? see my code below which I flowed the Jonathan’s example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
//
using System.IO; //fro the stream reader
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Collections.Concurrent;
using System.Collections;
//
var url = “http://TEST.my.com:33333/m3api-rest/execute/PMS230MI/Select;ma…. “;
var request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential(“gaston”, “myfakepwd”);
var response = (HttpWebResponse)request.GetResponse();
using(response) {
string responseValue = string.Empty;
if (response.StatusCode == HttpStatusCode.OK) {
StreamReader readert = new StreamReader(response.GetResponseStream());
string x = readert.ReadToEnd();
}
LikeLike
Gaston, I do not know the answer right now; I would have to look. What you are doing is all standard stuff, REST/JSON/C#, so you can get help on the Internet, nothing is proprietary. As for the validity of the JSON, it should be valid, you can check yours with JSONlint. I’m sorry that’s all I can help with right now.
LikeLike
Otherwise, there’s always the proprietary library https://m3ideas.org/2012/02/23/how-to-call-m3-api-from-net/
LikeLike
Put a break point at string x and see what is the content of x before parsing it to JSON. It could be that you are not getting proper response string.
LikeLike
This is one of my recent implementation. There is a difference in JSON response from different M3 Grid version so you may have to tweak it. Exception handling is changed in recent version of M3. It used to generate NOK in WebException which is no longer the case.
try
{
HttpWebRequest request = WebRequest.Create(url + “;returncols=” + returncols + “;maxrecs=” + maxrecords + “?” + inputValues) as HttpWebRequest;
request.Credentials = new NetworkCredential(username, password);
}
catch (WebException wex)
{
error = (wex.Response != null ? ((HttpWebResponse)wex.Response).StatusDescription : wex.Message);
if (error.ToUpper().Contains(“RECORD DOES NOT EXIST”))
{
error = “”;
}
// throw;
}
catch (Exception ex)
{
error = ex.Message;
}
LikeLike
Hi Virang,
I was missing basic settings on my header like “request.Accept = application/json” but with your illustration I was able to get it working.
Thank you,
Gaston
LikeLike
is there a way to filter results to only show the customers that are in STAT 90?
LikeLike
Only if the M3 API has a filter field as an input. You could use the Search API if you setup your Infor Enterprise Search, I think, look in your API repository.
LikeLike
where do I fine the API repository?
LikeLike
Easiest is MRS001, otherwise Infor Grid Management Pages > M3BE Management Pages > Tools > …
LikeLike
NOTE: The M3 API WS in M3 13.4 released September 2016 now supports CORS (Cross-Origin Resource Sharing, which was missing in the HTTP response), and supports Swager at m3api-rest/swagger/{MIProgram}. More information at M3 Core Infrastructure and Technology Release Notes Version 13.4.0 Published September 6, 2016.
LikeLike
Is there a way to set maxrecs to list all records? If i take maxrecs out it will only return 99 records max. If i add say 5000 ill get 5000 but I don’t always know the amount of records..
LikeLike
Set it to zero to return all records regardless of the amount of records
LikeLike
NOTE: There is documentation, code and examples for M3 API REST in the M3 API Toolkit at C:\MvxAPI\MIWS\
LikeLike
UPDATE: I recommend using the Chrome extension Postman https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop:

LikeLike
Hello,
I don’t work with M3 but I’m doing an integration with M3 Cloud. Our REST client is going to consume data from a custom M3 API program via REST call.
This custom program would export transactional data. In the worst case, they estimate that volume of data would be 1.500.000 transactional records.
Any experience with this kind of calls so can add feedback about potential issues exporting that amount of data from a custom M3 API program being called remotely with REST?
And let’s say we have 40 divisions running the same program (with different inputs). We would be talking about 50 millions of records in total from different calls…
For M3 Cloud, wouldn’t be any other method apart from the REST calls that would be more efficient? We can have java code on our side to call the API if needed.
Any feedback is more than welcome.
Thanks
LikeLike
Hi Francisco,
I did not work with tuning M3 performance, so I am not the best person to respond to your question. And I do not work with M3 anymore, so I am not aware of the latest answers for your question. And I used to work primarily with on-premise customers, not so much with cloud customers, so I do not have metrics for the cloud.
Nonetheless, my gut feeling tells me the M3 cloud is built with reliability, high availability, scalability and fail-over as top priorities, and comparatively, with performance as a secondary priority, not the reverse. That means your 1.5 million query will probably succeed, just not as fast as you may want it to.
My suggestions are the following:
1) Do some tests and find the throughput of your M3 (for example, you may find it can handle no more than 100 queries per second), and throttle your queries accordingly so they occur not faster than the server can respond to (for example, after every query, force a pause of 10ms, so that there are maximum 100 queries per second; your 1.5M query will complete in 1.5M/100 in seconds, that’s about four hours).
2) Query only static data such as product names, do not query transactional data like available-to-promise (although you said you need transactional data), because it is expensive to compute in terms of CPU, memory, disk access, and database.
3) Optimize your implementation by introducing a cache between client and server to prevent repeated queries from hitting the server. I do not know that there is a turn-key cache solution for M3 API, but there are plenty of cache software available on the Internet.
The Infor M3 team has a team dedicated to performance. Try getting in touch with them through your Infor channels (e.g. Infor project manager, technical project manager, Infor Support).
At the following link is some work I did in the past for adding data to M3 on-premise, not exactly your question, but you may find some answers: https://m3ideas.org/2016/03/17/poll-how-to-add-80-million-itemwarehouse-records-in-m3/
As for the entry points, I think with the cloud you only get the REST entry point. If it were on-premise, you would be able to call M3 directly with the binary M3 API protocol, or even faster, query the database itself.
Another idea: export the database (I don’t know if Infor offers that option for cloud customers), and query the offline database on your computer directly.
Hope it helps.
Thibaud
LikeLike
From where I am able to find list of API Program and API’s transaction.
LikeLike
I’m making a GET call from a mashup to a third party API that is returning JSON. However, I am not able to display the response in a mashup. Using Fiddler, I can confirm a JSON response. However, the Smart Office log says there is no response.
LikeLike
May I know how to set the timeout of M3 Rest API?
I need to run the following GLS840MI CtrlBatch but it reaches the time limts (150 seconds).
http://hostname:port/m3api-rest/execute/GLS840MI/CtrlBatch
Should I need to add the param readtimeoutmillis? If so, where can I place it?
Thanks
LikeLike
This wass great to read
LikeLike
Hi all,
Thank you for the detailed descriptions. It is a big help!
I would like to ask about the rest string when we have to send transactions to a blank division. I try to send DIVI=&… or DIVI=null&…
Can you please share how to do it in the correct way?
Thank you!
LikeLike
I haven’t worked with this in 10 years. I remember there was a trick to force a value to blank, but I don’t remember how. Try 3 asterisks *** or 3 question marks ??? .
LikeLike
Where can I find what all parameters are available like maxrecs, returncols, etc ?
LikeLike