Tag Archives: M3

M3 picking lists in Google Glass @ Inforum

I am very pleased to announce that after months of working here and there in the evenings voluntarily after work hours, I finally completed and presented both my demos of M3 picking lists in Google Glass and Augmented Reality at Inforum. They were a success. I showed the demos to about 100 persons per day during six days flawlessly with very positive reception. The goal was to show proof of concepts of wearable computers and augmented reality applied to Infor M3. My feet hurt.

Features

This is my second Glass app after the one for Khan Academy.

This Glass app has the following features:

  • It displays a picking list from Infor M3 as soon as it’s created in M3.
  • For each pick list line it shows the quantity (ALQT), item number (ITNO), item description (ITDS), and stock location (WHSL) as aisle/rack/level.
  • It displays the pick list lines as a bundle for easy grouping and finding.
  • It shows walking directions in the warehouse.
  • It has a custom menu action for the picker to mark an item as picked and to change the status of that pick list line in M3.
  • It uses the built-in text-to-speech capability of Glass to illustrate hands-free picking.
  • It’s bi-directional: from M3 to Google’s servers to push the picking list to Glass, and from Google’s servers to M3 when the picker confirms a line.
  • The images come from Infor Document Management (formerly Document Archive).
  • I developed the app in Java as an Infor Grid application.
  • I created a custom subscriber and added a subscription to Event Analytics to M3:MHPICL:U.
  • It uses the Google Mirror API for simplicity to illustrate the proof-of-concept.

I have been making the resulting source code free and open source on my GitHub repository, and I have been writing the details on this blog. I will soon post the remaining details.

Acknowledgements

I want to specially thanks Peter A Johansson of Infor GDE Demo Services for always believing in my idea, his manager Robert MacCrate for providing the servers on Infor CloudSuite, Philip Cancino formerly of Infor for helping with the functional understanding of picking lists in M3, Marie-Pascale Authié of Infor Pre-Sales for helping me setup and create picking lists in M3 and for also doing the demo at Inforum, Zack Makris of Infor Labs for providing technical support, Jonathan Amiran of Intentia Israel for helping me write the Grid application, and some people of Infor Product Development that chose to remain anonymous for helping me write a Java application for Event Hub and Document Archive. I also want to specially thank all the participants of Inforum whom saw the demo and provided feedback, and all of you readers for supporting me. And I probably missed some important contributors, thank you too. And thanks to Google X (specially Sergey Brin and Thad Starner) for believing in wearable computers and for accelerating the eyewear market.

Screenshots

Here below are the screenshots from androidcast. They show the bundle cover, the three pick list lines with the items to pick, the Confirm custom menu action, the Read aloud action, and the walking directions in the warehouse:

result0_ result1_ result2_ result3_ result3c_ result3r result4_

Vignettes

Here below are three vignettes of what the result would look like to a picker:

1 2 3

 

Inforum

Here are some photos at Inforum:

In the Manufacturing area:
20140917_162028_998

 

 

In front of the SN sign:
10704143_10152726242211873_7070689785986702196_n

 

Holding my Augmented Reality demo:
IMG_0007

Playing around with picking lists in virtual reality (Google Cardboard, Photo Spheres, and SketchFab):
bild 3

Playing around with picking lists in Android Wear (Moto 360):
20140915_110737_664_

 

That’s it! If you liked this, please thumbs up, leave a comment, subscribe to this blog, share around you, and come help me write the next blog post, I need you. Thank you!

Tagged , ,

Walking directions in a warehouse

Two years ago I had implemented a proof-of-concept that showed walking directions in a warehouse to complete a picking list. The idea is to visually represent what a picker has to do, and where they have to go, while calculating the minimum distance they have to walk. It will make it easier to get the job done for pickers that are not familiar with a warehouse, like temporary staff. And the business benefit is to minimize picking time, to make savings in labor costs, and to increase throughput. Also, we can save performance data on the ground, derive the gap between goals versus results, and use that as a feedback loop for continuous improvement of internal processes.

I had used my previous work on geocoding of stock locations in M3, and I had used Will Thimbleby‘s JavaScript implementation of the A* search algorithm to calculate and show the shortest path between two stock locations. I yet have to integrate the Traveling Sales Problem (TSP) algorithm for the entire picking list similar to my previous work on delivery route optimization for M3.

And there is more work to be done. For instance, the calculation responds quickly on my laptop for about 11 picking list lines, perhaps 13 with the ant colony optimization, but beyond that the calculation time grows exponentially beyond useful. Also, the calculation does not take into account bottleneck or collision avoidance for forklifts. Currently, this project is a great proof-of-concept to be further explored.

When Google Maps launched their outstanding Indoor Maps I had shelved my small project. But Google Indoor Maps requires the maps to be public which our M3 customers are reluctant to do. So for Inforum this week, I un-boxed my project and integrated it in my Google Glass demo. Here below are some screenshots of the project. I will be writing a series of posts soon on how to implement it. And I would like your feedback.

In future work, I will implement the same proof-of-concept using Google Indoor Maps.

planL

 

x y

Tagged , ,

Inforum 2014

I will be at Inforum 2014 next week. I will demo my Google Glass app with M3 picking lists. Come check it out at the M3 Labs booth in The Hub area. I will be with Marie-Pascale Authié and Peter A Johansson of Infor GDE Demo Services. My contact info is here.

Tagged , , ,

Get an image from Infor Document Archive with Java

For my project to get M3 picking lists in Google Glass, I had to learn how to retrieve in Java an image that is stored in Infor Document Archive. The technique is probably well explained and documented somewhere, but I don’t work with Document Archive so I didn’t know where to start. So as a starting point, I used one of the Java examples built-in Document Archive 10.1.x, and from there I adapted the code for the older Document Archive 10.0.x. I share my results here with you in case you too need to retrieve resources in Java from Document Archive and don’t know where to start. I will later use that code in my Infor Grid application to insert the picture of the item in the picking list in Glass.

Built-in examples

Document Archive 10.1.x is built-in with some examples in Java to connect to the server and retrieve resources. I compiled that code and it ran out of the box. I then used it as a basis to adapt the code to the older Document Archive 10.0.x for which I couldn’t find built-in examples. I will show you the code for both versions. I will also show how to encode the bytes to Base64 as I might need it later for the Google Glass Mirror API.

2

Code for Document Archive 10.1.x

Here is the Java source code for Document Archive 10.1.x:

import java.io.FileOutputStream;
import java.io.InputStream;
import com.infor.daf.icp.CMItem;
import com.infor.daf.icp.CMResource;
import com.infor.daf.icp.Connection;
import com.infor.daf.icp.SearchQueries;
import com.infor.daf.icp.SearchQuery;
import com.infor.daf.icp.Connection.AuthenticationMode;
import org.apache.commons.codec.binary.Base64;

public class Test {
	public static void main(String[] args) {
		try {
			String baseUrl = "https://hostname:26108/ca/";
			String username = "JOHNDOE";
			String password = "*******";
			String query = "/M3_ITEM_IMAGE[@M3_ITNO = \"ACME\"]";
			Connection conn = new Connection(baseUrl, username, password, AuthenticationMode.BASIC);
			conn.connect();
			CMItem item = CMItem.search(conn, new SearchQueries(new SearchQuery(query)));
			for(CMResource res : item.getResources().values()) {
				// to file
				FileOutputStream fos = new FileOutputStream(res.getFilename());
				InputStream is = res.getUrlStream();
				CMResource.streamData(is, fos, true);
				// to URL
				System.out.println(res.getUrl().toString());
				// to Base64
				byte[] bytes = CMResource.createByteArray(res.getUrlStream());
				System.out.println(new String(Base64.encodeBase64(bytes)));
			}
			conn.disconnect();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

Find the Java library for Document Archive 10.1.x, icp.jar, and the required open source libraries httpclient, httpcore, commons-logging, commons-codec, and jaxen:

<LifeCycle>\<host>\grid\<grid>\grids\<grid>\applications\DocumentArchiveCM\lib\

3

Compile and run the code with:

javac -extdirs . Test.java
java -cp icp.jar;httpclient-4.3.2.jar;httpcore-4.3.1.jar;commons-logging-1.1.1.jar;commons-codec-1.6.jar;jaxen.jar;. Test

The application will connect to Document Archive, will search the image by query, will retrieve the resource and will save it to a file, for example to JPEG.

The application will also show the URL to the resource, for example:

https://hostname:26108/ca/api/resources/93+3+ICM8+icmnlsdb13+M3_ITEM_IMAGE59+26+A1001001A14F13A20952E6009918+A14F13A20952E600991+14+1027?$token=aHR0cDov&#8230;

The application will also show the Base64-encoded bytes of the resource.

Code for Document Archive 10.0.x

Here is the Java source code for the older Document Archive 10.0.x. It is very similar to the newer code above, the package names are different (intentia instead of infor) and some methods are older. I had to decompile icp.jar to learn and adapt the code.

import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;
import org.apache.commons.codec.binary.Base64;
import com.intentia.icp.common.CMItem;
import com.intentia.icp.common.CMResource;
import com.intentia.icp.common.Connection;

public class Test {
	public static void main(String[] args) {
		try {
			String baseUrl = "https://hostname:25194/ca/";
			String username = "JOHNDOE";
			String password = "*******";
			String query = "/ESA_ItemImage[@ESA_ItemNumber = \"ACME\"]";
			Connection conn = new Connection(baseUrl, username, password);
			conn.setAuthMode(Connection.BASIC);
			conn.connect();
			CMItem item = CMItem.search(conn, query);
			Iterator it = item.getResources().iterator();
			while (it.hasNext()) {
				CMResource res = (CMResource)it.next();
				// to XML
				System.out.println(res);
				// to file
				if (res.getEntityName().equals("ICMBASE")) {
					InputStream is = res.retrieveResource(conn);
					FileOutputStream fos = new FileOutputStream(res.getOrgFileName());
					CMResource.streamData(is, fos);
					// to Base64
					byte[] bytes = CMResource.createByteArray(res.retrieveResource(conn));
					System.out.println(new String(Base64.encodeBase64(bytes)));
				}
			}
			conn.disconnect();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

Find the Java library for Document Archive 10.0.x, icp.jar, and the required open source libraries httpclient, httpcore, commons-logging, commons-codec, jaxen, and commons-io:

<LifeCycle>\<host>\grid\<grid>\grids\<grid>\applications\DocumentArchiveCM\lib\

3

Compile and run the code with:

javac -extdirs . Test.java
java -cp icp.jar;jaxen-1.1.1.jar;httpclient-4.2.2.jar;httpcore-4.2.2.jar;commons-io-2.4.jar;commons-logging-1.1.1.jar;commons-codec-1.7.jar;. Test

The application will connect to Document Archive, will search the image by query, will retrieve the resource and will save it to a file, for example to JPEG.

The application will also show the XML of the resource, for example:

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<res>
    <pid>85 3 ICM8 icmnlsdb7 ICMBASE58 26 A1001001A14F20A45344I4341718 A14F20A45344I434171 13 300</pid>
    <id>A1001001A14F20A45344I43417</id>
    <compId>A14F20A45344I43417</compId>
    <version>1</version>
    <entityName>ICMBASE</entityName>
    <type>0</type>
    <acl>
        <name>defaultACL</name>
    </acl>
</res>

The application will also show the Base64-encoded bytes of the resource.

 

That’s it. If you liked this, please click Like, leave me your thoughts in the comments below, and share your solutions by writing posts.

Also, please join the campaign and sign the petition to Infor so they make more of their source code available such that we can all learn more and make better solutions.

 

Thank you.

 

Tagged ,

Hacking Infor Grid application development (part 5)

Here is the fifth part in the series Hacking Infor Grid application development. This time I will show you how to deploy a web application archive, WAR file. It is trivial and quick as the Infor Grid automatically deploys it.

Create the archive

Simply ZIP the contents of the web application into a WAR file:
1

File & folder structure

Move the WAR file into the webapps folder of the Grid application, and delete the rest of the webapps content (WEB-INF, JSP, etc.) so that there is only the WAR file left:

[...]
|
\---webapps
        HelloWorld.war

Deploy

Create the Grid application archive GAR and deploy it in the Grid as usual. The Infor Grid will automatically deploy the WAR file under webapps:
2

GitHub

I updated the GitHub repository with a Tag for part5.

Petition

Remember to join the campaign in asking Infor Product Development to release their Grid Development Tools and tutorials and to make their source code available so we can learn how to make good Grid applications.

Meanwhile, remember this is a hack with the intention to learn and progress.

Tagged , ,

Hacking Infor Grid application development (part 4)

Continuing the series Hacking Infor Grid application development, today I will show: how to decompile Java applications, how to write to the Grid application log files, how to read Grid application properties, how to secure the Java web application, and how to start playing with the HelloWorld app on GitHub. Remember this is currently a hack, it is at your own risk, there are limitations as discussed on part 1, and please join the campaign and sign the petition to Infor Product Development for making their source code available. The intention is to learn the internals of the applications we use every day, push their limits, and develop great software.

Decompile Java applications

First of all, check your licenses to determine if you are legally allowed to decompile the Java application you have in mind.

For this series I decompiled the Infor Grid core grid-core-1.11.27.jar to understand how to write to the Grid application log files and how to get Grid application properties. I also often decompile applications like M3_UI_Adapter (MNE), Event Hub, M3 Web Services, and Infor Process Automation to understand how they work, make enhancements, troubleshoot, and find bugs. All the JAR files are located in LifeCycle Manager at E:\Infor\LifeCycle\<host>\grid\<grid>\grids\<grid>\applications\. It is already common practice to decompile Infor Smart Office with RedGate Reflector (C#) to push the limits of Smart Office scripts and Smart Office SDK applications. I wish the source code of all applications were available.

For decompiling Java applications, I use the great Java Decompiler and its GUI. I simply drag and drop a JAR file and it automatically decompiles all files recursively:
1

Write to Grid application log files

To write to the Grid application log files, get the logger and log at the information, warning, error, debug, or trace levels:

import com.lawson.grid.util.logging.GridLogger;
[...]
private static final GridLogger log = GridLogger.getLogger(HelloWorld.class);
[...]
log.info("Yay, I'm writing to the log file :)");
log.warn("Hey, it feels hot in here!");
log.error("Ouch, that hurt :(");
log.debug("Useful data 48656C6C6F576F726C64");
log.trace("Wooohooo 01001000 01100101 01101100 01101100 01101111 00100000 01010111 01101111 01110010 01101100 01100100 00100001");

Set the log levels accordingly:

Check the result in the Grid application log file:
3

The log files are also directly in the log folder for use with a tail program or so:
5

Read Grid application properties

Declare Grid application properties in the application deployment descriptor GRID-INF\application.xml:

<property name="message">Hello Wrrrld!</property>

6

Then, read the properties with:

import com.lawson.grid.node.properties.GridProperties;
[...]
GridProperties p = ModuleContext.getCurrent().getProperties();
String message = p.getProperty("message");
log.info("The message is: " + message);

Re-deploy the application and check the application properties:
7

And here is the result:
4

Secure Java web application

To secure the Java web application in WEB-INF\web.xml:

<security-constraint>
    <web-resource-collection>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>grid-user</role-name>
    </auth-constraint>
</security-constraint>

Set the User and Role Mappings in the Grid Configuration Manager accordingly:8

Restart the application.

The Grid will now challenge for authentication:
9

HelloWorld app on GitHub

I published the HelloWorld Grid application source code on my GitHub repository so you can play along, learn, and contribute your code, and I added tags – part1, part2, part3, and part4 – so you can download the source code that corresponds to the desired part in this series:
10

Summary

That was an illustration of how to decompile Java applications, how to write to the Grid application log files, read Grid application properties, secure the Java web application, and start playing with the HelloWorld app on GitHub. Remember to play responsibly, know the limitations, sign the petition, and contribute your findings to the community.

That’s it! If you liked this, please click Like, write your comments, click Follow to subscribe to the blog, share around you, and author your own ideas. Thank you.

Tagged , ,

Hacking Infor Grid application development (part 3)

In the series Hacking Infor Grid application development, today I will illustrate how to add a JAR file to an Infor Grid application and a JAR file to a dynamic web application in that Grid application (which is trivial).

JAR for Grid application

First, create a simple Java library:

package net.company.your.library1;

public class HelloWorldLibrary1 {
	public static String getMessage() {
		return "Hello World sample library for my Grid application";
	}
}

Then, compile it with:

javac net\company\your\library1\HelloWorldLibrary1.java

Then, add it to a JAR file with:

jar cvf HelloWorldLibrary1.jar net\company\your\library\HelloWorldLibrary1.class

Then, call that library from the Grid application with:

[...]

import net.company.your.library1.*;

public class HelloWorld implements ApplicationEntryPointEx {

	public boolean startModule(ModuleContext paramModuleContext) {
		System.out.println(HelloWorldLibrary1.getMessage());
		return true;
	}

	[...]
}

Then, recompile that Grid application as usual with:

javac -cp grid-core-1.11.27.jar;. net\company\your\HelloWorld.java

Here’s the result in the command prompt:
1.1

Then, create a jars folder in the Grid application and add the JAR file to it:
1.3

The default folder for JAR files is jars. It seems other Infor Grid applications use folder lib instead. We can change the classpath in the Grid application’s properties, for instance the application M3UIAdapter has JAR files in a folder lib:
3.2b

Then, replace the Grid application:
1.2

Then, restart the Module in the Grid Management Pages:
b4

We can see the result in the logs:
1.4

JAR for dynamic web application

Now let’s create a second Java library for the dynamic web application of the Grid application; this is classic J2EE and trivial:

package net.company.your.library2;

public class HelloWorldLibrary2 {
	public static String getMessage() {
		return "Hello World sample library for my dynamic web application";
	}
}

Then, compile it and add it to a JAR file as shown above.

Then, let’s use that library from the servlet (or from a JSP):

[...]

import net.company.your.library2.*;

public class HelloWorldServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		[...]
		out.println(HelloWorldLibrary2.getMessage());
	}
}

Then, recompile the servlet as usual.

Here’s the result in the command prompt:
2.1

Then, create a lib folder in the WEB-INF folder of the dynamic web application:
2.3

Then, replace the servlet (or JSP):
2.2

And refresh the servlet (or JSP) to see the result:
2.4

Summary

That was how to add a JAR file to an Infor Grid application and a JAR file to a dynamic web application in that Grid application (which was trivial). Next time I will find out how to add a WAR file.

Also, remember this is a hack that currently has limitations due to our lack of knowledge of how to develop good applications for the Infor Grid, as discussed in part 1 of this series.

Also, remember to join the campaign and sign the petition to Infor Product Development for making their source code available.

That’s it! If you like this, please click the Like button, leave a comment below, subscribe to this blog, share around you, and write about your own ideas here. Thank you.

Tagged , ,

Hacking Infor Grid application development (part 2)

As a prolongation of my previous post on Hacking Infor Grid application development, this time I will show you how to add a simple HelloWorld servlet to your Infor Grid application.

HelloWorld servlet

Create a simple HelloWorld servlet:

package net.company.your;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<body>");
		out.println("HelloWorld servlet! The time is " + new Date());
		out.println("</body>");
		out.println("</html>");
	}
}

Compile it with the Servlet API library that you can find in the LifeCycle Manager runtime:

E:\LifeCycle\<host>\grid\<grid>\runtimes\1.11.27\resources\servlet-api-2.5.jar
javac -cp servlet-api-2.5.jar WEB-INF\classes\net\company\your\HelloWorldServlet.java

1 2

Deployment descriptor

Create a simple deployment descriptor web.xml:

<?xml version="1.0" encoding="utf-8" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">

	<display-name>HelloWorld Servlet</display-name>
	<description>My HelloWorld servlet</description>

	<servlet>
		<servlet-name>HelloWorldServlet</servlet-name>
		<servlet-class>net.company.your.HelloWorldServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>HelloWorldServlet</servlet-name>
		<url-pattern>/sup</url-pattern>
	</servlet-mapping>

</web-app>

File & folder structure

Set the following file & folder structure in a WEB-INF directory:

\---HelloWorld
    ...
    \---webapps
        \---HelloWorld
            ...
            \---WEB-INF
                |   web.xml
                |
                \---classes
                    \---net
                        \---company
                            \---your
                                    HelloWorldServlet.class

b3

With Eclipse

You can also use Eclipse to create the servlet and deployment descriptor and to compile:
e8

Deploy

You can create and deploy your servlet as a new application, or you can add WEB-INF to an existing application in LifeCycle Manager at:

E:\LifeCycle\<host>\grid\<grid>\grids\<grid>\applications\HelloWorld\webapps\HelloWorld\WEB-INF\

If you choose hot deployment, reload the Grid application module:

b4

 

 

Result

You can now test the result in a browser:
b5

And you can check the logs:
b6

Summary

That was how to add a simple servlet to your Infor Grid application. Next time, I will explore how to add JAR files and how to deploy a WAR file.

That’s it! Like + comment + subscribe + share.

Tagged , ,

Hacking Infor Grid application development

I just learned a technique to develop a custom application for the Infor Grid. The idea is to build a simple J2EE app using the Infor Grid as an application server. My understanding is that the Infor Grid is closed, and that this technique is currently unofficial, undocumented and unsupported. I will label this technique as a hack for now until we establish a recommended path.

History

In the past, official products such as Movex Workplace (MWP) and IBrix were built for IBM WebSphere Application Server (WAS), and software developers that needed custom J2EE applications unofficially deployed their code in that same WAS; it was a somewhat symbiotic relationship. With the switch to the Infor Grid, WAS is gone and replaced by a cloud of distributed servers with embedded Jetty – that is a leap forward for M3 – but there is no official solution for custom apps.

As a workaround, we continued to sideload our JSP and HTML files in the MNE folder of the Grid alongside Smart Office Scripts; that was a parasitic relationship subject to potential loss during upgrades. There is also the option to install our own separate application server like Microsoft IIS, but there are advantages to having a unified solution. I know of the Grid Development Tools for Eclipse and the Grid Application Developer Guide and tutorials, but to my knowledge they are internal to Infor products and not available for developing custom applications.

A former colleague of mine found an alternative workaround. He told me he had managed to create a GAR file for the Infor Grid, so now he has an application called “intentia” where he puts the JSP and HTML files instead of the MNE folder. He said it involved decompiling one of the Grid applications and then compiling it back with the new application name. I invited him to write a blog post about his solution, but he responded because it is unofficial he did not want to post it and wanted to remain anonymous. After a couple of email exchanges discussing a disclaimer, he accepted that I post his solution and acknowledge him for his contribution. So thank you, Jonathan Amiran of Intentia Israel.

Call for action

This is my call to Infor Product Development. If you are reading this, please jailbreak the Grid, release the tools and documentation and embrace the community. Help us reach a mutually beneficial relationship where great ideas can get out of anonymity and flourish. I understand there are risks involved in opening up a sensitive system to all sorts of inexperienced developers and misuse as that leads to you having to assume the role of support and troubleshooting until you can prove negligence on the part of the third-party developer. But don’t be closed. Share the good practices with us, certify us, or if it’s a risk, build a system that defends against byzantine failures from third-party developers.

For you readers, please call your Infor representatives and tell them the importance of being open and working together. Meanwhile, please understand the following hack has not yet been peer reviewed so it may be either favorable or damaging.

Find the Grid core

First, go to your LifeCycle Manager server and find the JAR file for the Grid core:

E:\LifeCycle\<host>\grid\<grid>\resources\grid-core-1.11.27.jar

b1

Create the application entry point

Then, create a simple Java class that implements ApplicationEntryPointEx:

package net.company.your;

import com.lawson.grid.node.application.ApplicationEntryPointEx;
import com.lawson.grid.node.application.ApplicationEntryPointEx.GlobalState;
import com.lawson.grid.node.application.ApplicationEntryPointEx.RemainingTaskCount;
import com.lawson.grid.node.application.ModuleContext;

public class HelloWorld implements ApplicationEntryPointEx {

	private boolean isInitialized = false;

	public boolean startModule(ModuleContext paramModuleContext) {
		return true;
	}

	public void onlineNotification() {
		this.isInitialized = true;
	}

	public void offlineNotification() {
		this.isInitialized = false;
	}

	public void stopModule() {
		this.isInitialized = false;
	}

	public ApplicationEntryPointEx.RemainingTaskCount getRemainingTaskCount() {
		return null;
	}

	public ApplicationEntryPointEx.GlobalState getGlobalState() {
		if (this.isInitialized) {
			return new ApplicationEntryPointEx.GlobalState(true, new String[] { "OK" });
		}
		return new ApplicationEntryPointEx.GlobalState(false, new String[] { "Initalizing" });
	}
}

And compile it with:

javac -cp grid-core-1.11.27.jar net\company\your\HelloWorld.java

Create the application deployment descriptor

Create the application deployment descriptor in a file GRID-INF\application.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<application xmlns="http://schemas.lawson.com/grid/configuration_v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" typeName="HelloWorld" version="10.1.0.1" xsi:schemaLocation="http://schemas.lawson.com/grid/configuration_v3 http://schemas.lawson.com/grid/configuration_v3">
	<description>HelloWorld Grid Example</description>
	<moduleDefinitions>
		<moduleDefinition typeName="HelloWorld" entryPointClass="net.company.your.HelloWorld" horizontallyScalable="false" verticallyScalable="false">
			<webApp name="HelloWorld" distributedSessions="false" sessionAffinity="true" />
		</moduleDefinition>
	</moduleDefinitions>
	<nodeTypes>
		<nodeType name="HelloWorld">
			<module typeName="HelloWorld" />
		</nodeType>
	</nodeTypes>
	<connectionDispatchers/>
	<properties>
		<propertyList name="grid.module.classpath">
			<value>classes</value>
			<value>jars/*</value>
		</propertyList>
		<property name="grid.jvm.maxHeapMB">64</property>
	</properties>
</application>

Add the resources

Add the static (HTML, images, CSS, JavaScript, etc.) and dynamic (JSP) resources in a webapps sub-folder, for example index.jsp:

<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html>
   <body>Hello World! This is my first Grid application. The time is <%=new Date()%></body>
</html>

With the Infor Grid we don’t have a web server plugin to optimize serving static from dynamic content by URL path so I keep everything in the same folder.

Zip to a Grid archive

Create a file and folder structure like so:

+---classes
|   \---net
|       \---company
|           \---your
|                   HelloWorld.class
|
+---GRID-INF
|       application.xml
|
\---webapps
    \---HelloWorld
            image.png
            index.jsp
            script.js
            static.html
            style.css

ZIP the contents of the folder into a file and rename the file extension to .gar (make sure to ZIP the contents of the folder and not the folder itself or you will end up with an incorrect folder structure):

b1_

With Eclipse

You can also use Eclipse for this.

For that, create a new Java project, change the Default output folder from /bin to /classes, and add the grid-core as an external JAR:
b2

Create the file and folder structure as explained above:
b3_

ZIP the entire contents of the folder and change the file extension to .gar as explained above:
b4

Install the application

  1. Go to the Grid Management PagesAdvancedConfigurationConfiguration ManagerApplications and select Install New Application:
    b5
  2. Click Upload, browse to your GAR file, and click Upload:
    b6
  3. Select the hosts on which to deploy the app and click Finish:
    b7
  4. It will say “Operation completed.” Click Close:
    b8
  5. It will say “The application has no bindings defined”. Select Fix this problem:
    b9
  6. Enter Min 1 and Initial 1 and click Add Binding:
    b10
  7. It will say “The web application ‘HelloWorld’ of module ‘HelloWorld’ has no context root defined”. Select Fix this problem:
    b11
  8. Enter a Context Root Name and click Add:
    b12
  9. It will say “There are unsaved changes”. Click Save:
    b13
  10. Click Save to confirm the changes:
    b14
  11. The application is now deployed on the Infor Grid:
    b15

Result

Now we can try the application by going to /HelloWorld/:
18

My understanding of the Infor Grid is that applications run in a node, and that a node runs in its own JVM (probably to avoid collateral damage if the app crashes). We can confirm this by finding our app in the server’s Windows Task Manager:
b16

 

Updates

[UPDATED 2014-07-24]

At this point, you can update your files (JSP, HTML, JavaScript, etc) directly in the LifeCycle Manager deployment folder, refresh the browser, and the server will serve the new version of your files. This is the same type of updates we do with Smart Office scripts. If you do this, remember to backup your changes.

E:\LifeCycle\<host>\grid\<grid>\grids\<grid>\applications\HelloWorld\webapps\HelloWorld\

Limitations

Because of the distributed topology, fault tolerant, load balanced, scalable, and redundant nature of the Infor Grid, it is our responsibility as grid application developers to design them correctly. But we don’t yet know how to do that on the Infor Grid. Remember to call Infor about that. So keep it simple for now:

  • Make the application stateless until we find out how to store and share and synchronize state and make distributed sessions on the Grid.
  • Don’t make direct connections to any servers with network sockets until we find out how to abstract the communication with the Grid routers.
  • Don’t change any shared resources anywhere to avoid race conditions until we find out how to run critical sections on the Grid.
  • Don’t expect to do parallel programming since we don’t yet know how to communicate with multiple instances of the application.
  • Don’t persist data on disk as we don’t yet know how to abstract storage for failover and redundancy.

Summary

This was a hack to start developing simple applications on the Infor Grid. This technique is unofficial, undocumented and unsupported. It is our responsibility as developers to design applications correctly. Ask Infor to open up and support us.

That’s it! If you liked this, thank Jonathan, click Like, leave your comments below, subscribe to this blog, share with your peers, ask Infor to be open and embrace the community, and be responsible with your code. Thank YOU.

Tagged , , ,

How to decrypt network traffic from Infor Grid

Here is a technique to intercept and decrypt the TLS (HTTPS) network traffic from the Infor Grid using Wireshark and the server’s private keys.

Why does it matter?

This technique is useful for troubleshooting products like M3 Web Services (MWS) and Infor Process Automation (IPA) which don’t log the HTTP requests and responses in their entirety. For instance, MWS Runtime can optionally dump the SOAP requests and SOAP responses but misses the HTTP request headers and HTTP response headers, and IPA only logs the HTTP response body but misses the HTTP request’s header and body and the HTTP response header, and neither MWS nor IPA let us hook to a proxy such as Fiddler. Sometimes it’s necessary to troubleshoot the HTTP requests and responses in their entirety. For example, I’m currently troubleshooting for a customer the case of a rogue white space somewhere in a request that’s throwing a syntax error down stream in a parser, and I need to chase the bug down by analyzing the hexadecimal values of the bytes, and for that I need un-encrypted traffic.

We could use Wireshark to intercept all network packets but if the traffic is encrypted with TLS (HTTPS) it’s unreadable. In public-key cryptography, a client and server initiate a TLS connection using asymmetric cryptography, and then switch to symmetric cryptography for the rest of the session. Fortunately, the Wireshark SSL dissector can decrypt traffic if we give it the server’s private keys. I had previously showed this technique a long time ago to decrypt Lawson Smart Office traffic and more recently to intercept un-encrypted IPA traffic. This time I update the technique for encrypted traffic of the Infor Grid.

Don’t get exited about hacking and don’t freak out about security because this technique is only available for those administrators that have access to the servers private keys and passwords.

Server’s private keys and passwords

First, we will need to find the Infor Grid’s private keys and passwords. I don’t do Grid installations so I don’t know where the keys are stored (if in the same path on any Grid, or if at a path defined by the installer), nor how the keys are generated (if automatically by the Grid, or if manually by the installer). In my case, I was testing on two different Grids and I found the keys in LifeCycle Manager (LCM) server at these two different paths:

D:\Infor\LifeCycle\<host>\grid\<grid>\grids\<grid>\secure\
E:\Infor\LifeCycle Manager\LCM-Server\grid\<grid>\keyStore\

This non-consistency tells me the path is defined by the installer.

Here is a screenshot:
b1

The paths contain many files of type *.ks and *.pw. The KS file type is a keystore encrypted with a password. The PW file type is the password in clear text; it looks encrypted but it’s just long random clear text. In my second Grid, there were about 50 pairs of files where the file names seem to follow a specific naming convention. That tells me the keys and passwords are generated automatically by the Grid.

Export and convert the private key

Now that we have the keystores and the passwords, we need to export the private key from the keystore and convert it to a format supported by Wireshark. For that, we can use the keytool of the JRE to export and OpenSSL to convert, or use KeyStore Explorer that will both export and convert.

Here’s with the keytool (export to PKCS12) and OpenSSL (convert to PEM):

keytool -importkeystore -srckeystore mykeystore.ks -destkeystore myexportedkey.p12 -deststoretype PKCS12
openssl pkcs12 -in myexportedkey.p12 -out myexportedkey.pem -nocerts -nodes -passin file:mykeystore.pw

b2

And here’s with the KeyStore Explorer (directly to PEM):
b3

Now we have a file with —–BEGIN PRIVATE KEY—–:
7

Import the private key in Wireshark

Now we import the key in Wireshark > Edit > Preferences > Protocols > SSL and set the Infor Grid server’s IP address, port and private key (PEM):
b4

Intercept and decrypt traffic

Now we are ready to intercept and decrypt traffic, for example we can go to the Grid Management Pages with HTTPS:
b5

Then we filter for ssl, see the decrypted traffic, the key exchange, and Follow SSL Stream:
b6

Summary

That was a technique to intercept and decrypt network traffic of the Infor Grid using Wireshark and the server’s private keys which is useful for troubleshooting purposes. This technique is only available to the administrators that have access to the servers.

If you know of a simpler technique please let me know.

That’s it. Please like, comment, subscribe, share. Thank you.

Tagged , , , , ,
Follow

Get every new post delivered to your Inbox.

Join 140 other followers