Developing H5 Client scripts – Part 2

I am learning to develop H5 Client scripts for a customer; see my previous post for the beginning.

To give back

My customer Chris Bullock thought client side scripts are pretty awesome, and said it would be cool if there was a library of what other M3 users have done. I proposed to post the script here with his permission, and he agreed for the love to give back.

Functional requirement

The requirement is to develop a script for M3. Purchase Order. Receive Goods – PPS300/E in H5 Client that pulls values from a related field in M3 and populates it in another field. Basically getting attribute values from purchase order line, concatenating their values, then populate that in the Lot number field on the purchase order receipt screen.

More specifically, when the user is receiving goods for a purchase order in PPS300/E, the script should automatically set the Location (WHSL) and Lot number (BANO) with the correct values to not let the user enter incorrect values by accident even with the F4-Browse:

Script

Here is the preliminary script I developed:

var PPS300E_BANO = new function () {
	var color = 'rgb(250, 255, 189)';
	this.Init = function (scriptArgs) {
		var controller = scriptArgs.controller;
		var content = controller.GetContentElement();
		var IBITNO = content.GetElement('IBITNO')[0]; // Item number
		var IBPUNO = content.GetElement('IBPUNO')[0]; // Purchase order number
		var IBPNLI = content.GetElement('IBPNLI')[0]; // Purchase order line
		var WLWHSL = content.GetElement('WLWHSL')[0]; // Location
		var WRBANO = content.GetElement('WRBANO')[0]; // Lot number
		// ensure the Item group is MAT
		ScriptUtil.ApiRequest('/execute/MMS200MI/Get;returncols=ITGR?ITNO=' + encodeURIComponent(IBITNO.value), response => {
			var ITGR = response.MIRecord[0].NameValue[0].Value.trim();
			if (ITGR !== 'MAT') {
				return;
			}
			if (!WLWHSL.readOnly && WLWHSL.value === '') {
				// hard-code the Location to YARD
				WLWHSL.value = 'YARD';
				// color the field
				WLWHSL.style.backgroundColor = color;
				WLWHSL.nextElementSibling.style.backgroundColor = color;
			}
			// get the Attribute number
			ScriptUtil.ApiRequest('/execute/PPS200MI/GetLine;returncols=ATNR?PUNO=' + encodeURIComponent(IBPUNO.value) + '&PNLI=' + encodeURIComponent(IBPNLI.value), response => {
				var ATNR = response.MIRecord[0].NameValue[0].Value.trim();
				// get the attributes
				ScriptUtil.ApiRequest('/execute/ATS101MI/LstAttributes;returncols=AALF?ATNR=' + encodeURIComponent(ATNR), response => {
					// calculate the Lot number
					var BANO = '';
					response.MIRecord.forEach(e => BANO += e.NameValue[0].Value.trim());
					if (!WRBANO.readOnly && WRBANO.value === '') {
						// set the Lot number
						WRBANO.value = BANO;
						// color the field
						WRBANO.style.backgroundColor = color;
						WRBANO.nextElementSibling.style.backgroundColor = color;
					}
				}, (error, message) => MainController.Current.ShowDialog([error, message]));
			}, (error, message) => MainController.Current.ShowDialog([error, message]));
		}, (error, message) => MainController.Current.ShowDialog([error, message]));
	};
};

Development time

When I develop the script, I alternate between pieces of code in Chrome’s JavaScript console and debugger, and the assembled script in a text editor, iteratively until it’s ready, testing along the way with ScriptName.Init({ 'controller': getActiveController() }):

Result

The result is the following, the script sets the Location and Lot number, and highlights them in yellow with the same color as the web browser’s autofill color to indicate that it autofilled the values:

At this point, the user can verify the values and click Next (or press ENTER) to persist the values in M3.

Problems

There are several problems with this script:

  1. The script is not able to tell apart whether the user entered the record with Option 1-Create or with Option 2-Change. In the former case, the script should set the values because the values have never been set; but in the latter case, the script should not set the values because they have already been set. I tried controller.Response.ControlData.Bookmark.Opt but it returns "2" for both Options 1-Create and 2-Change which is wrong. We are running M3 UI Adapter version 10.3.1.0.147. In a thread with Reah, she said if we upgrade M3 UI Adapter to version 10.3.1.0.161, I will be able to use controller.GetMode() instead. To be continued.
  2. To make M3 API calls, I use ScriptUtil.ApiRequest. But as of M3 UI Adapter version 10.3.1.0.195, that method is deprecated and replaced by MIService. See my thread with Reah. To be continued.

Usability

There is this corner case in usability, unrelated to H5 Client scripts:

Initially, the customer wanted me to set the fields and disable them, no matter what. That works if the user creates a new record with Option 1-Create. But if the user enters an existing record with Option 2-Change and there are already values that another user has previously set, what should the script do? Should the script assume the values are correct and let it be? In which case the script could have guessed incorrectly and leave incorrect values behind (false negative). Or should the script assume the values are incorrect and reset them? In which case the script could have guessed incorrectly and contradict the intention of the previous user (false positive). Furthermore, if the script does reset the values, how will the user know those are new values to persist? Will highlighting in yellow be enough? Or will the user incorrectly assume those are the values currently persisted in M3? I have to read more about WebKit’s autofill design decisions and learn from it. For now, I apply the weakest enforcement: if the field is blank, set it; otherwise, do not; and never disable it.

PENDING

There are several pending issues:

  • Upgrade M3 UI Adapter to the latest version
  • Use controller.GetMode() to tell apart Option 1-Create and 2-Change
  • Replace ScriptUtil.ApiRequest by MIService
  • Usability: disable the two fields while calling the M3 API, indicate activity (spinning wheel), revert when done, cancel if gone (ENTER, F3, F5, F12)
  • Add exception handling: if == null, try/catch, if response.Message, if !response.MIRecord
  • Compose the promises sequentially with request1.then(request2).then(request3) or Promise.all([request1, request2, request3]) instead of nesting them with request1({ request2({ request3() }) })
  • Use JavaScript async/await for ease of source code reading
  • Use Visual Studio and TypeScript as recommended by the M3 H5 Development Guide (H5 Script SDK)

Conclusion

That was my preliminary script for H5 Client while I am learning how to develop them. I still have to learn more about H5 scripts and autofill, solve current problems, and address pending issues.

Special thanks to my customer Chris Bullock.

Infor M3 open source platform announcement thing

Let’s talk about the Infor M3 open source platform announcement thing.

Announcement

In 2015, Infor announced: “The latest Infor M3 solution now features an open source based Linux platform as a deployment choice to reduce total cost of ownership for customers.”

It is admirable to support open source software, so please join us in congratulating Infor.

What about free/libre software?

It would have been more admirable to support free/libre software. The open source movement and the free/libre software movement are related but distinct; freedom is more important than open source.

Supported platform

The following documentation, M3 Core 13.4 Installation Guides Red Hat Linux > M3 Core Installation Planning Guide – RHEL > Introduction and overview > Installation Scenarios > Recommendations, says M3 Core is now available as an option on “Red Hat Enterprise Linux as operating system and Postgres Plus Advanced Server as database […] If the M3 Database Server is installed on Red Hat Enterprise Linux, the Postgres Plus Advanced Server must be used.”

Red Hat Enterprise Linux

Red Hat Enterprise Linux is a commercial product. Its source code is open and provided at no cost, but it is not provided in compiled form. To use it, we have to either buy it or go through the difficult compilation and derivative processes.

As a side note, Red Hat has controversy in the community with their non-free/libre licensing and inclusion of binary blobs [1] and as such is not endorsed by the Free Software Foundation [2].

EnterpriseDB Postgres Plus Advanced Server

EnterpriseDB Postgres Plus Advanced Server is also a commercial product, even though based on the open source PostgreSQL, it is provided at cost, and its source code is not available:

Technical reason?

Besides understandably not being able to support every combination of platform, are there any technical reasons for M3 to HAVE TO use Red Hat Enterprise Linux and EnterpriseDB Postgres Plus Advanced Server, and not Fedora, CentOS, Debian, Ubuntu, Suse, PostgreSQL?

Business collaboration?

Does it have anything to do with Infor’s joint collaboration with Red Hat and EnterpriseDB for Infor LN?

Wasn’t there something similar with the Intentia and IBM alliance [3] [4], where Movex Workplace HAD TO use the Enterprise Edition of IBM WebSphere Application Server for no apparent technical reason?

What about the community?

Infor uses a lot of free/libre software on free/libre licenses such as BSD, GPL and Apache. For example, this is the (not cleaned-up) list of about 100 projects used by one of the components of M3, the Infor Grid:

antlr antlr-runtime aopalliance-repackaged asm asm-commons asm-tree bcmail-jdk16 bcprov-jdk16 commonj.sdo commons-daemon commons-fileupload commons-logging commons-math3 ctivation cxf-api cxf-rt-bindings-soap cxf-rt-bindings-xml cxf-rt-core cxf-rt-databinding-jaxb cxf-rt-databinding-xmlbeans cxf-rt-frontend-jaxws cxf-rt-frontend-simple cxf-rt-management cxf-rt-transports-http cxf-rt-ws-addr cxf-rt-ws-policy cxf-rt-ws-rm cxf-rt-ws-security decision-trees derbyclient drools-compiler drools-core ecj eclipselink ehcache-core geronimo-javamail_1.4_spec groovy-all hk2-api hk2-locator hk2-utils izpack-api izpack-tools jackson-core-asl jackson-jaxrs jackson-mapper-asl jackson-xc javassist javax-websocket-client-impl javax-websocket-server-impl javax.annotation-api javax.el javax.inject javax.persistence javax.servlet-api javax.servlet.jsp javax.servlet.jsp-api javax.servlet.jsp.jstl javax.websocket-api javax.ws.rs-api jcommander jersey-client jersey-common jersey-container-servlet jersey-container-servlet-core jersey-core jersey-guava jersey-json jersey-media-json-jackson jersey-media-multipart jersey-multipart jersey-server jersey-servlet jetty-annotations jetty-continuation jetty-http jetty-io jetty-jndi jetty-jsp jetty-plus jetty-schemas jetty-security jetty-server jetty-servlet jetty-servlets jetty-util jetty-webapp jetty-xml jna jna-platform joda-time jsr166 jt400_jdk16 knowledge-api knowledge-internal-api linked-binaries liquibase log-viewer mail maven-shared-utils mimepull mockito-all mvel2 neethi ojalgo ojdbc6 org.apache.taglibs.standard.glassfish org.eclipse.jdt.core osgi-resource-locator postgresql scripting-client slf4j-api slf4j-grid sqljdbc4 stax2-api stringtemplate tasks validation-api websocket-api websocket-client websocket-common websocket-server websocket-servlet windowsjnasecurity woodstox-core-asl wsdl4j wss4j xml-resolver xmlbeans xmlschema-core xmlsec

Does Infor contribute back to the free/libre software community? Individual Infor developers probably contribute to free/libre software with bug fixes and documentation (congratulations). But I have not seen Infor as a company officially sponsor events, fund projects, contribute code, or provide developers to free/libre software. I do not know that they do or that they do not. If you know, please leave me a comment.

Discussion

It is admirable that Infor provides M3 based on an open source platform as an option, it illustrates Infor’s commitment to the cloud and open platforms. However, the narrative is not clear as the required “open source” platform actually requires commercial products, at cost, that have restrictive licenses, with closed source code, possibly due to a business collaboration. For-profit corporations can choose to be commercial, closed source, and collaborate with partners, to fund product development and to protect intellectual property, but then the narrative should be made clear. Is there a technical reason? Is Infor misappropriating “open source”? Is Infor giving back to the free/libre software community? What do you think? Let me know in the comments below.

Developing H5 Client scripts – Part 1

The day came I have to develop a script for Infor M3 H5 Client with M3 API calls for a customer. This post will add to my previous post, to Scott’s three previous posts, and to Karin’s previous post.

Disambiguation

Scripts for H5 Client are written in the JavaScript programming language (ECMAScript). Scripts for Infor Smart Office are written in the JScript.NET programming language. Programs for M3 are written in the Java programming language. Despite the similarities, the three languages are different. Smart Office scripts will NOT execute in H5 Client; you will have to re-write most of the code and be familiar with functional programming, jQuery, Deferred, Promises, etc.; it is like back in the days of IBrix.

Documentation

Here is the M3 H5 Development Guide:

Conversion

Here are some tips to convert a Smart Office script to an H5 Client script:

Example of a minimal script for Smart Office:

package MForms.JScript {
	class Test {
		public function Init(element: Object, args: Object, controller : Object, debug : Object) {
			debug.WriteLine('Hello, World!');
		}
	}
}

The equivalent script for H5 Client:

var Test = new function () {
	this.Init = function (scriptArgs) {
		console.log('Hello, World!');
    };
};

Various ways to get a field and its value compared to norpe’s guide for Smart Office:

var controller = scriptArgs.controller;
var host = controller.ParentWindow;
controller.FieldMap.WRCUNM[0]
controller.GetElement('WRCUNM')[0]
H5ControlUtil.GetFieldValue('WRCUNM')
ScriptUtil.FindChild($, 'WRCUNM')[0]
ScriptUtil.FindChild(host, 'WRCUNM')[0]

UPDATE 2017-06-06: According to the H5 Development Guide, the above is not the recommended API, but controller.GetContentElement().GetElement("WRCUNM") instead.

Example to call an M3 API in H5 script:

ScriptUtil.ApiRequest('/execute/CRS610MI/LstByNumber', result => console.log(result), (error, message) => console.log([error, message]))

UPDATE 2017-06-06: According to the H5 Development Guide, the above is not the recommended API, but MIService.Current.execute|executeRequest instead, but I get error ‘MIService is not defined’; perhaps I do not have the latest version of H5 Client.

Chrome Developer tools

Use the Google Chrome Developer tools:

Use it for the list of global variables, code completion, type reflection, console, dynamic execution, debugger, network monitor, DOM, styles:

Pause execution to introspect global variables:

UPDATE 2017-06-06: In the JavaScript console, you can get the current controller with getActiveController() or MainController.Current.Instances.host_X where X is the controller number.

Administrative tool

Use the administrative tool to import/export H5 scripts:

UPDATE 2017-06-06: To update a script, simply re-import it, click Yes to override, and refresh the M3 panel with Actions > Refresh-F5; there is no cache thus no need to add the version number in the script file name unlike Smart Office.

Execute

Attach the script to the panel as usual at Tools > Personalize > Scripts:

Select Actions > Refresh-F5 to load the script:

Use the JavaScript debugger statement in your script to pause execution and invoke the Chrome debugger:

Call M3 API, and refer to my previous post:

Conclusion

That was a quick look at how to develop scripts for Infor M3 H5 Client including calls to M3 API.

Thanks to Scott Campbell of Potato IT for the first look.

Please like, comment, subscribe, share, come author with us, or look at other ways to contribute.

Related documentation

M3 MI Data Import for Web Services (MDIWS)

I just learned the existence of the M3 MI Data Import for Web Services (MDIWS), which is the equivalent of the traditional M3 Data Import (MDI) but using the M3 API REST/JSON endpoint instead of the traditional proprietary binary endpoint.

The tool and documentation are straightforward, so I will just promote it here with some screenshots:

Here are the executable and documentation:

Here is a sample semicolon separated CSV file with data, no header:

Here is the tool in action:

As a reference, here is the traditional M3 Data Import tool:

Thanks Björn P. for the tool.

That’s it!

Access Control on Grid Management Pages

It is my great pleasure to be an author of M3 Ideas. Thanks very much for Thibaud’s invitation.

I put my foot into M3 water five years ago and M3 Ideas has been giving me great help in developing my technical skills since then. It is my great honor to share my work and make contributions to it.

The issue of access control on grid management pages has been a trouble to me for nearly two years. The installation web page of ISO, in our case, http://BE Server:19005/LSO/index.html is very close to the grid information page, http://BE Server:19005/grid/info.html and the grid management pages, http://BE Server:19005/grid/ui/#. It is fairly easy for a user with web skills to figure out the latter two web pages and he/she can explore and check basically all the grid information.

Although the grid management pages requires credentials to log on to be able to start or stop a process, a user actually can try the default account,  and the famous password, which are mentioned by the Companion, to get full control. This is not a joke, but a real case. It makes M3 system really vulnerable. Even if users do not make any actions to it, there is no sense and it is ridiculous to disclose all the system information to the public.

We did raise InforXtreme case for Infor to fix it. However, they could not give a robust solution after a long time discussion. The reason is the grid management pages and LSO is sharing the same Java process in the BE server. If you check the TCP connections and processes using netstat in the BE server, you would find there is no way to tell which connection is from the grid management pages visit or from LSO request, which is the biggest technical challenge.

After many tests it seems the most feasible solution is to analyze each incoming and outgoing TCP packet in the BE sever to see whether there are any patterns. If we can be 100% sure that a remote IP address is visiting the grid management pages according to the patterns, then we can apply IPSec to block it automatically. Although it means that IP address cannot use LSO as well, it can be sorted out by manually removing it from the black list given a promise is made not to visit again. If the promise is broken, there is no second chance for them. It is something like Damocles’ Sword.

So I made a Console Application using C# as follows.


using System;
using System.Collections.Generic;
using SharpPcap;
using SharpPcap.LibPcap;
using System.Text;
using System.Net.Mail;
using System.Diagnostics;

namespace CA_Grid
{
class CA_Grid
{
static List<string> jsIPList;

static void Main(string[] args)
{
jsIPList = new List<string>();

var varDevices = CaptureDeviceList.Instance;

if(varDevices.Count<1) { Console.WriteLine("No devices were found on this machine"); return; } Console.WriteLine("The following devices are available on this machine:"); Console.WriteLine("----------------------------------------------------"); Console.WriteLine(); int i = 0; foreach(var varDec in varDevices) { Console.WriteLine("{0}) {1}", i, varDec.Description); i++; } Console.WriteLine(); Console.Write("-- Please choose a device to capture: "); int varChoice = 0; if (!int.TryParse(Console.ReadLine(), out varChoice) || varChoice>=i)
{
Console.WriteLine("The device is not valid!");
return;
}

ICaptureDevice varDevice =varDevices[varChoice];

varDevice.OnPacketArrival += Device_OnPacketArrival;

varDevice.Open();
varDevice.Filter = "ip and tcp";
Console.WriteLine();
Console.WriteLine("-- Listening on {0}, hit 'Ctrl-C' to exit...",varDevice.Description);

varDevice.Capture();

varDevice.OnPacketArrival -=Device_OnPacketArrival;
varDevice.Close();
}

private static void Device_OnPacketArrival(object sender, CaptureEventArgs e)
{
try
{
string varMIP = "BE Server IP Address";
if (e == null || e.Packet == null)
return;

if (e.Packet.Data == null)
return;

var varPacket = PacketDotNet.Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);
if (varPacket == null)
return;

if (varPacket.GetType()!=typeof(PacketDotNet.EthernetPacket))
return;

var varIP = (PacketDotNet.IpPacket)varPacket.Extract(typeof(PacketDotNet.IpPacket));
if (varIP == null)
return;

string varDIP = varIP.DestinationAddress.ToString();
string varSIP = varIP.SourceAddress.ToString();

if (varSIP != varMIP)
return;

var varTCP = (PacketDotNet.TcpPacket)varPacket.Extract(typeof(PacketDotNet.TcpPacket));
if (varTCP == null)
return;

if (varTCP.PayloadData == null)
return;

var varData = Encoding.UTF8.GetString(varTCP.PayloadData);

if (varData == null)
return;

string varDPort = varTCP.DestinationPort.ToString();
string varSPort = varTCP.SourcePort.ToString();

bool varHTTPS = varSPort == "443";
bool varHTTP = varSPort.Contains("19005") && varData.Contains("text/html") && varData.Contains("gzip");
if ((varHTTPS || varHTTP) && !jsIPList.Contains(varDIP))
{
jsIPList.Add(varDIP);

string varWay = varHTTP ? "HTTP" : "HTTPS";
string varM1 = "----------------------------------------------------";
string varM2 = string.Format("{0}, {1}, {2}", DateTime.Now, varDIP, varWay);
string varM3 = "Original IP packet: " + varIP.ToString();
string varM4 = "Original TCP packet: " + varTCP.ToString();
string varM5 = "Original TCP Header: " + varData;

if (varDIP != "IP to Exclude")
{
using (var varProcess = new Process())
{
varProcess.StartInfo.FileName = "cmd.exe";
varProcess.StartInfo.UseShellExecute = false;
varProcess.StartInfo.RedirectStandardInput = true;
varProcess.StartInfo.RedirectStandardOutput = true;
varProcess.StartInfo.RedirectStandardError = true;
varProcess.StartInfo.CreateNoWindow = true;
varProcess.Start();

string varCommand = "netsh ipsec static add filter filterlist=\"IP_Filter_Grid\" srcaddr=" + varDIP + " dstaddr=me protocol=TCP mirrored=No";
varProcess.StandardInput.WriteLine(varCommand);
varProcess.StandardInput.WriteLine("exit");
varProcess.StandardInput.AutoFlush = true;
varProcess.WaitForExit();
}
}

Console.WriteLine(varM1);
Console.WriteLine(varM2);
Console.WriteLine();
Console.WriteLine(varM3);
Console.WriteLine();
Console.WriteLine(varM4);
Console.WriteLine();
Console.WriteLine(varM5);
Console.WriteLine();

using (var varSMTP = new SmtpClient("Mail Forwarder IP Address"))
{
using (var varMail = new MailMessage())
{
varMail.From = new MailAddress("from address");
varMail.To.Add("my e-mail address");
varMail.Subject = "Unauthorised Access to Infor Grid Management Pages";
varMail.Body = varM1 + "\r\n" + varM2 + "\r\n" + "\r\n" + varM3 + "\r\n" + "\r\n" + varM4 + "\r\n" + "\r\n" + varM5;
varSMTP.Send(varMail);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

SharpPcap can be downloaded from GitHub. It has two Dlls, SharpPcap for capturing packets and Packet.Net for packet analysis. WinPcap needs to be downloaded and installed into the BE server. There is no need to restart the server after installation.

The console application is running in an asynchronous mode, so there is trivial impact on the M3 performance.

The script starts with detecting all the devices first. Then it asks for a device number to listen to. Each device could be a network adapter. A filter is applied to the device for IP and TCP packets. An event to capture the packets is attached to the device. Finally the listening starts.

The event handler has the key logic.

1. Two patterns to find the visit to the grid management pages.

(1) HTTPS: The source port would be 443.

(2) HTTP: The packet contains text/html and gzip.

HTTPS communication is encrypted, so we cannot analyze the packet content. Fortunately LSO never uses the port of 443.

2. If the destination IP address is not on the white list, then it would be added to the IPSec filter list. We need to manually create a IPSec policy and a IPSec filter & Action beforehand.

3. Console would output the captured information.

4. An e-mail alert would be sent with the captured information.

5. A global variable of jsIPList is used to keep the IP addresses already captured to avoid repeated alerts.

Then it works. There were three IT users from local division, who still tried to access to the grid management pages on Monday as usual. Then they found they could not visit it any more, as well as LSO. So far there are no normal users to be blocked, no matter whether they run M3 programs, or download LSO from the installation web page. All of sudden the two-year headache is gone.

If you have the similar concerns to me, I hope the above solution would give you a ride. If you have got any issues using the solution, please reply to this post or send me an e-mail at warren.dahai.hou@gmail.com.

Finally my heartfelt appreciations to all the authors on this website, as well as the author of SharpPcap and Packet.Net.

Infor e-Commerce XML Gateway

Here is my first test of the Infor e-Commerce XML Gateway.

It is something like https://example.com/infor-ecom/common/gateway.jsp?user=u&password=p&object=o&method=m&request=r

About

The XML Gateway is an API in Infor e-Commerce, for third-party software to make requests to e-Commerce, e.g. to create an order, to get an invoice, or to delete an item. The requests are XML documents, in the proprietary IdealXML schema, sent to e-Commerce over HTTP, targeting the e-Commerce Business Objects and methods.

History

The XML Gateway has existed since the early days of Ideal Seller and e-Sales about 20 years ago:
9 3___ 8

License

The current part number is M3C-SGX Infor e-Commerce B2B XML Application Adapter:
1

Is MEC required? No.

Despite what the Infor sales and documentation say, Infor ION, Infor M3 Enterprise Collaborator (MEC), and the e-Commerce Extension for MEC, are NOT required to use the XML Gateway; the XML Gateway is built-in e-Commerce. MEC is suggested for it provides useful features such as development tools, transaction management, persistence, XML transformation, connection pooling, logging, monitoring, management, etc. Apart from that, you can use any other software that does XML over HTTP. Besides, if you were to use MEC, it would have to be the ancient version 9.0.4.0 which is probably not compatible with the MEC you already have for M3:

Documentation

There is some documentation available. The greatest amount of information is located in the Infor e-Commerce Development Studio User’s Guide, and there is some in the Developer’s Guide, Administration Guide, and Configuration Guide for Infor ION:
5__ 6__ 7 14

Business Objects

The business objects and methods of the XML Gateway are listed in the Business Center at Gateway > Requests:

The code is automatically generated from the e-Commerce Development Studio, e.g. Business Object Item and method GatewayDetails to get the details of an item by input parameter ItemID:
40 41 42 43 44

Source code

For the curious of us, we can peak at the source code.

The source code starts at /infor-ecom/src/common/gateway.jsp and includes the automatically generated JSP fragment Gateway.jspf:
10

And the e-Commerce Java library common-10.0.0.jar includes the class com.intentia.iec.businessobject.input.XMLRequest which parses the XML input:
11

That is sufficient to start peaking at source code.

Furthermore, the e-Commerce Extension for MEC ecom-extension-9.0.0.jar includes the class com.intentia.iec.meci.http.HttpRequester which sends the HTTP request:
12

And the ION Connector’s MEC Custom Channel iecprotocol-2.0.0.jar includes the class com.intentia.ec.channel.IECGateway which also sends an HTTP request:
13

Samples

There are sample XML requests in the proprietary IdealXML schema located at C:\Program Files (x86)\Infor\Infor e-Commerce 14.9.1.0\XML Sample Documents\IdealXML 2.0\:
16 17

XSD

The XML requests are validated with the proprietary IdealXML Schema located at Infor e-Commerce Development Studio\Infor e-Commerce Extension for Enterprise Collaborator\dist\e-Commerce\request.xsd:
15

The Development Studio User’s Guide has a chapter The iXML document format.

XSLT? No.

The latest version of the XML Gateway does NOT provide XSL Transformation (XSLT); it may have been included in the early versions when it was ASP, not anymore. You can easily develop a proxy JSP that does XSLT and that forwards the request to gateway.jsp. I will explore this in a future post. Otherwise, if you use MEC and the e-Commerce Extension for MEC, you can do XSLT in MEC as usual.

Setup

To setup the XML Gateway:

Enable the desired Business Object Method (they are all enabled by default), e.g. Item.GatewayDetails:
33

In the desired Role (e.g. I created role Third-party software), select the Feature Element Gateway User:

In the desired User B2B, select that role:

Still, I am not able to reproduce the setup for another user, and I do not know why.

HTTP request

The HTTP request is of the form:

POST https://example.com:443/infor-ecom/common/gateway.jsp HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: example.com:443
Content-Length: 123

user=u&password=p&object=o&method=m&request=r

Where:

  • I recommend HTTPS (secure), not HTTP (insecure).
  • The scheme, hostname and port number are those of your e-Commerce, e.g. https://example.com:443.
  • The method can be either GET (with the parameters in the URL) or POST (with the parameters in the request body), but I advise against GET because the user and password would be in the URL, and nodes along the way may store the URL in clear text in the logs; I recommend POST instead.
  • The URL path is /infor-ecom/common/gateway.jsp
  • The media type is application/x-www-form-urlencoded
  • The input parameters user and password are for authentication. Note: the XML Gateway does not accept HTTP Basic authentication.
  • The input parameters object and method are for the business object.
  • The input parameter request is the entire <IdealXML> content.
  • All parameters must be URL-encoded.

Test

For my test, I will call Item.GatewayDetails to get the details of item 9412-0111:

Here is the corresponding IdealXML that I created from the sample XML:

<?xml version="1.0" encoding="UTF-8"?>
<idealXML version="2.0" id="Thibaud test">
   <commands>
      <command>
         <request>
            <params>
               <param name="@LanguageCode">en</param>
            </params>
            <search>
               <bindings operand="or">
                  <bindings operand="and">
                     <binding attribute="ItemID" value="9412-0111" operator="eq"/>
                  </bindings>
               </bindings>
            </search>
         </request>
      </command>
   </commands>
</idealXML>

Notes:

  • I removed the <login> and <password> from the <credential> as they are ignored.
  • I removed the <command>’s name and method attributes as they are ignored.

Result

Here is the result in raw and parsed view:

The XML response is the following, successful, with the requested item details:

<?xml version="1.0" encoding="UTF-8"?>
<resultset object="Item">
   <row Description="For ITRAN GE BLACK INKJET CART 10/CTN" GroupCode="OE" GroupName="Impact Inking" IsActive="Y" IsDangerous="0" IsEmphasized="N" ItemCode="normal" ItemCodeID="normalItemCode" ItemField1="WELLS FARG" ItemField2="201502861.00" ItemField3="" ItemField4="12/99" ItemField5="Y" ItemID="9412-0111" Key="8685" MainCategoryID="59052" MinimumQty="0.0000" ModularQty="0.0000" Name=" INK JET EQUIPMENT" SupplierID="" UnitCodeID="CTItemUnit" Weight="2.4940">
      <Category CategoryID="59052" CategoryName=" INK JET EQUIPMENT" InternalName=" INK JET EQUIPMENT (59052)"/>
      <Text Description="For ITRAN GE BLACK INKJET CART 10/CTN" LanguageID="de" Name=" INK JET EQUIPMENT"/>
      <Text Description="For ITRAN GE BLACK INKJET CART 10/CTN" LanguageID="en" Name=" INK JET EQUIPMENT"/>
      <Text Description="For ITRAN GE BLACK INKJET CART 10/CTN" LanguageID="fr" Name=" INK JET EQUIPMENT"/>
      <Text Description="For ITRAN GE BLACK INKJET CART 10/CTN" LanguageID="it" Name=" INK JET EQUIPMENT"/>
   </row>
</resultset>

Troubleshooting

For troubleshooting:

Inspect the HTTP response headers that indicate either success:

XMLGatewayStatusCode: 200
XMLGatewayMessage: OK

Or an error:

XMLGatewayStatusCode: 401
XMLGatewayMessage: Login failed
XMLGatewayStatusCode: 403
XMLGatewayMessage: Access to requested object or method not allowed
XMLGatewayStatusCode: 500
XMLGatewayMessage: Internal error while executing Gateway method

The Development Studio User’s Guide has a chapter Gateway error messages, with codes, but the codes do not match this.

It is better to look in the logs at C:\IBM\WebSphere\AppServer\profiles\AppSrv01\logs\server1\SystemOut.log:
22

Problems

  • The user and password are echoed in clear text in the logs, it is insecure:
    23
  • The XML Gateway accepts requests over plain HTTP (instead of HTTPS) even though the request contains the user and password and other private data in clear text, it is insecure.

Future work

  • Force HTTPS (secure) by default, disable HTTP (insecure).
  • Do XSL transformation (XSLT).
  • Finish reproducing the setup.
  • Try the e-Commerce Extension for MEC.
  • Try the WSDL and the Infor e-Commerce Web Service Specifications for Synchronous Calls.
  • Try other Business Objects and methods.
  • Try custom Business Objects and methods.
  • Understand more about the bindings.

Conclusion

That was my first test of the XML Gateway in Infor e-Commerce, for third-party software to make requests to e-Commerce, e.g. to place an order or get the details of an item. I have more work to do if I want to master the XML Gateway.

That’s it!

Please like, comment, subscribe, share, participate.

Subversion in Infor e-Commerce Development Studio

Here is an illustration of how to setup Apache Subversion for source control in Infor e-Commerce Development Studio, such that multiple developers can work on the same e-Commerce project at the same time, for team development.

Problem

The problem is that by design an Eclipse workspace cannot be shared, i.e. there can only be one developer using a workspace at a time, others will get the message “Workspace in use or cannot be created, choose a different one”:
0

Small teams can setup multiple workspaces, one per team member, share the same source code, and mutually agree on which non-overlapping files to work on, being careful to not touch each other’s files. This is workable in small teams, but is not scalable for large teams or inexperienced team members as there is risk of data loss.

The correct solution is to setup source control software to manage that.

Objective

Supposing there a N team members doing e-Commerce development, we will setup one Subversion server & repository, and N e-Commerce Development Studios & workspaces all using the same Subversion repository. In my case, there are three developers, Thibaud, Chris, and Madan, thus I will setup three workspaces to use the same Subversion respository.

Documentation

There is a dedicated chapter in the Infor e-Commerce Development Studio User’s Guide:

Even better, there is a dedicated document in the M3 Adaptation Kit Version Control Guide, not for e-Commerce, but similar setup:
2_

What about Git?

I wish I could use Git, instead of Subversion. I believe Git would work for e-Commerce just as well as Subversion does. The e-Commerce Development Studio requires Subclipse during installation; I could install Subclipse just to surpass that, then ignore it, and install Git. However, I asked Infor Support if Git would work with e-Commerce, and they responded it has not been tested and certified to completely work, and should problems arise due to an effect from using Git, they cannot provide support for it. So I will skip Git for now, and I will try it another time.

1. Subversion server

Install a Subversion server of your choice (e.g. Apache Subversion, TortoiseSVNVisualSVN):
3

Create the users (e.g. THIBAUD, CHRIS, MADAN):
4

Create a new repository, and save the Repository URL in your notes:
5_ 5__

Schedule a regular backup of the repository, e.g.:

The Subversion repository is now ready to host the e-Commerce projects.

2. Bootstrap

Choose one user (e.g. Thibaud) to bootstrap the e-Commerce projects (e.g. B2C and B2B) to the Subversion repository for the rest of the team (e.g. Chris, Madan).

I make the assumption that the Eclipse workspace is private (non-shared), e.g. C:\Users\THIBAUD\workspace\ , that it already has the e-Commerce Development Studio fully setup, and that the e-Commerce projects (e.g. B2C, B2B) are fully setup (see previous post):

Note 1: If the projects are in a shared workspace, e.g. C:\e-Commerce\workspace\, and you want to move them to the private workspace, simply cut and paste the project folder from the shared workspace to the private workspace.

Note 2: Don’t make a duplicate (copy) of a project, because after we bootstrap the project to the Subversion repository, any duplicate of that project would be out of reach of Subversion and deprecated.

Go to Eclipse > Window > Open Perspective > SVN Repository Exploring, create new Repository Location, and enter the URL of the Subversion repository created earlier, with the username and password of the workspace’s user (e.g. THIBAUD):

For each project (e.g. B2C, and B2B), do the following:

Select > Team > Share Project:
7_

Select repository type SVN:
7__

Use existing repository location:
7___

Change folder to trunk:
7____

Enter a commit comment, e.g. Initial import:
7_____

Wait for the synchronization to finish:
7_______

Select Team > Commit:

Select everything except the following files and folders, and enter a commit comment, e.g. Initial commit:
extensions/businessobjectmethods.jar
src/com/
businessobject_timestamps.txt

Wait for the SVN Commit to finish:
8 8_

Each file now has a revision number, date, and author:

Verify in Team > Show history:
8_____

Verify the repository in the Subversion server:
8___

The project is now ready for the rest of the team to checkout.

Repeat for each project (e.g. B2C, B2B).

3. Other team members

Now we can setup the Eclipse workspaces and projects for the rest of the team (e.g. Chris, Madan).

Regarding the Eclipse IDE itself, you can choose to use a private install for each user, a shared install for all users, or a shared configuration, see the Eclipse multi-user installs.

I make the assumption that each user has the e-Commerce Development Studio fully setup, with a private workspace, e.g. C:\Users\CHRIS\workspace\ , and with no e-Commerce project (e.g. no B2C, B2B), like so:
9

Setup the SVN Repository for this user and password (e.g. CHRIS):
9_

Expand the trunk, and checkout the projects (e.g. B2C, B2B):
9__

Leave the default values and click Next:
9___

Leave the default workspace and click Finish:
9____

Wait for the SVN Checkout to finish:

Verify the result:
9______

Select the Active project:
10_

Go to Business Objects tab, click Generate Stub Codes, and wait for it to finish:
10__ 10___

That will create the files:
extensions/businessobjectmethods.jar
src/com/
businessobject_timestamps.txt

10____

If not, you may have to clean, re-build, and/or refresh the project.

This team member’s environment is now all set.

Repeat for each remaining team member.

4. Everyday development

For everyday development:

Set the Active Project as usual:
10_

Select Team > Update to HEAD to update the project with the latest changes of everyone else:
11

Make the changes you need to any of the files (e.g. JSP, CSS, HTML), and save your changes:
11_

Deploy the application or presentation as needed:
11_____

Test your changes in e-Commerce as usual:
11___

Repeat as needed: make changes, deploy, test.

When done, select the files you modified, select Team > Commit, and enter a comment of your changes; ensure you select only the files you changed, nothing else:
11__ 11____

Verify in the SVN Repository > Show History.

Now, when the other team members select Team > Update to HEAD, they will see your changes in their workspace.

Future work

Next time, I would like to try with Git instead of Subversion.

Conclusion

That was an illustration of how to setup Subversion in e-Commerce for a team to work on the same project at the same time. The first user bootstraps the project to the Subversion repository, and the other users checkout that project from the repository.

Special thanks to Wayne Liu of Infor and Marina Baldan of CAD IT for the help.

That’s it!

Please like, comment, subscribe, share, author.

Relate articles