Continuous integration of Mashups #2 – Reverse engineering

I will try to explain what happens internally in the various Infor applications when we deploy Mashups. I need to understand this so I can streamline the deployment for my needs. Here is my artistic rendering of the current flow:

Disclaimer

I will reverse engineer the applications by static analysis. This exposes the internals of the Infor applications, which are not supposed to be used by anyone other than Infor Product Development, and which may change with future versions of the applications. So the result is informative only. Also, I am not allowed to show any proprietary source code – I will just show snippets of decompiled code – and I will hide sensitive information such as hostnames, IP addresses, and passwords.

1. Mashup Designer > Generate > Lawson application

It all starts in Infor Smart Office > Mashup Designer > Generate Package > Smart Office > Lawson Application:
1

Reverse engineering

That MenuItem is created with the following event handler:
2

It then follows this stack of methods:

Mashup.Designer.MainWindow.IComponentConnector.Connect
 Mashup.Designer.MainWindow.OnFileDeploy
  Mashup.Designer.FileDeploymentWindow.OnSave
   Mashup.Designer.DeploymentHelper.CreateAppStream

Note: If instead we clicked the other MenuItem to generate a Mashup, it would follow this other stack of methods which doesn’t overlap with the previous:

Mashup.Designer.MainWindow.OnFileDeployMashup
 Mashup.Designer.DeploymentHelper.SaveMashupFile
  Mashup.Designer.DeploymentHelper.ValidateManifest
 Mango.UI.Services.Mashup.Internal.PackageHelper.CreatePackageFromManifest

The previous methods eventually create a .lawsonapp file which is a ZIP archive of the .mashup file and an app.config file:

8

The Mashup file is itself another ZIP archive of metadata files, the manifest, the XAML source code, and optional profile and resource files:
4

\package\services\metadata\core-properties\core.properties.xml:

\_rels\ .rels:
7

\[Content_Types].xml:
5

2. LifeCycle Manager > Admin Products > Upload

Then, we upload the .lawsonapp file to Infor LifeCycle Manager (LCM) > Admin > Upload Products > Upload (I switched Mashups, so the screenshots don’t match):

The result is:
9_____

It creates the Mashup folder in LCM-Server\products\ with a file productinfo.xml and unzips the .lawsonapp file in the components sub-folder:
12

Reverse engineering

A quick search of the upload popup with the command findstr /c:"Select packages to upload" /s *.class in the LCM Client folder gives us the class D:\Infor\LifeCycleManagerClient\LCM-Client\plugins\com.lawson.lifecycle.application_3.2.1.201406300711\com\lawson\lifecycle\application\editors\adminview\ProductsPage.class with method uploadPackages() which calls the interface com.lawson.lifecycle.application.upload.UploadPackage.upload(monitor):
10

A quick search of the success message “Package successfully uploaded to LifeCycle Manager Server” gives us classes Cloud9UploadPackage.class which implements the interface and JavaInstallerUploadPackage.class in folder D:\Infor\LifeCycle Manager\LCM-Client\plugins\com.lawson.lifecycle.application_3.2.1.201406300711\com\lawson\lifecycle\application\upload\

And a quick search in the LCM Client and Server logs gives us com.lawson.lifecycle.server.command.Cloud9App_UploadPackage:
11

Java Decompiler was not able to decompile these methods, so I used Krakatau decompiler in Python by Robert Grosse:

$ python decompile.py -path "C:/Program Files/Java/jre1.8.0_91/lib/rt.jar;LCM-Client/configuration/org.eclipse.osgi/bundles/9/1/.cp/lib/lcm-common-3.2.1.jar;LCM-Client/plugins/com.lawson.lifecycle.client_3.2.1.201406300711.jar" -skip LCM-Server/client/plugins/com.lawson.lifecycle.application_3.2.1.201406300711.jar

13

The method Cloud9UploadPackage.upload() gets a connection to the server, and uploads the file remotely. Then I kinda lost track of who creates the folder components folder and the file productinfo.xml. But it looks like it’s all about files and folders, nothing in a database, nothing cached in memory.

Let’s move on. This is becoming too dense.

3. LifeCycle Manager application install/upgrade and repeat per environment

Then, we install the product on the desired environment (e.g. DEV, TST):

14_14__14___14____14_____

Note: Once the Mashup is installed, we can upgrade it to a new version, and repeat the steps for another environment:
23

4. Apache Ant tasks

The install logs show it executed three remote Apache Ant scripts:

templates/cloud9_install.vm
templates/cloud9/install.vm
templates/cloud9_install_relation.vm

15
17
18

5. Apache Velocity templates

The Ant tasks are compiled from Apache Velocity templates at D:\Infor\LifeCycle Manager\LCM-Server\products\Infor_Applications_1.0.0\templates\:

The scripts have control structures (if-then-else, for-loop) and getters to replace property values:
30
19

I think this is it, com.lawson.lifecycle.common.command.RemoteScript_Run:
33

And com.lawson.lifecycle.server.command.RemoteAntScript_Run:
34

6. Remote code

LCM Server will execute code from tasks.jar at D:\Infor\LifeCycle Manager\LCM-Server\products\Infor_Applications_1.0.0\tasks\ :
20

Here we see the first reference to a database, the LCM Server database.

There are also these methods in D:\Infor\LifeCycle Manager\LCM-Server\products\LSO_10.2.1\tasks\tasks.jar and mangoserver_10.2.1.0.16.gar\lib\mangoserver-app-10.2.1.0.16.jar:

com.lawson.lifecycle.mango.DeployLawsonApplication.execute()
com.lawson.lifecycle.mango.GridLCMTask.deployLawsonApplication()
com.lawson.lso.installationpoint.InstallationPointManager.AddLawsonApp()

31 32

7. Apache Derby

The LCM Server database is an Apache Derby database that LCM uses with the Hibernate object-relational mapping framework:
21

The JDBC connection parameters are in the file lcm.properties at D:\Infor\LifeCycle Manager\LCM-Server\ :

hibernate.connection.driver_class=org.apache.derby.jdbc.EmbeddedDriver
hibernate.connection.username=*****
hibernate.connection.password=*****
hibernate.connection.url=jdbc:derby:lcmdb
derby.drda.portNumber=*****
database.read.user=*****
database.read.password=*****
database.name=lcmdb

We can see the Mashup in the product registration table:
22

8. Smart Office

Smart Office gets a copy of the Mashups, we can see them in the My Local Applications and Mashup File Administration tools:

It is populated by Mango.Core.Storage.FileStorageCategory.ListAllCategoryFileInfo:
25

9. MangoServer

That calls ListAllCategoryFileInfo in the SOAP web service /mangows/CategoryFilesManager?wsdl:
26

10. Grid database

MangoServer uses the Grid distributed database – an H2 database – where it duplicates the Mashups contents as blobs and sets authorization (SECURITY=1 means Access=Public, and SECURITY=0 means Access=None):
27

You can find the JDBC connection information at Infor ION Grid Management Pages > Grid DB Broker > View application configuration:
28

You can also use the embedded H2 console application:
29

11. Local Storage

Finally, the Smart Office client that each user starts for each environment will download the Mashup blobs and save them in the local storage:
35

12. Repository

Then there is this repository in the MangoServer grid application with more metadata about the Mashups: D:\Infor\LifeCycle\host\grid\DEV\grids\DEV\applications\MangoServer\Client\Repository\LawsonApps\ :
36 37

Summary

What a rodeo.

Deploying a Mashup globally involves Smart Office client/server, Mashup Designer, metadata and lots more metadata, LiceCycle Manager client/server, remote file copy, Apache Velocity templates that compile into Apache Ant scripts that execute remote code to add records to the Apache Derby database of LifeCycle Manager, then MangoServer gets duplicates of the Mashups from the Grid H2 database via SOAP web services, and finally Smart Office client stores local copies in the user’s computer.

The number of copies that must be kept in sync is 2 + p + pq where p is the number of environments and q is the number of users (1 copy from the developer, 1 copy in the LCM files & folders, p copies in MangoServer H2 databases, and pq copies in local storage). That’s normal for software development, except I did not expect the additional copy in the MangoServer database.

Future work

Next time, I will explore the MangoServer web services to upload Mashups.

Conclusion

My original intention in reverse engineering the internals of Mashup deployment was to learn how to develop a command line to streamline Mashup deployment and simplify it from thousands of clickks down to a few double-clicks; see my previous post for details.

But after all this rodeo, I conclude it would be a bad idea. There are too many moving pieces, too much to learn that’s not part of my job, and Infor Product Development may change it at any time. I need to go back to the drawing board and find a new approach.

At least it was a good learning exercise.

That’s it!

Please leave me a comment, slap a Like, subscribe to follow my experiments, share around you, and come write the next idea with us. This is a volunteer-based blog to help each other in the M3 community. Thank you for your support.

Related posts