Customizing RedPrairie RF Forms
Java Based MTF Forms
Overview
As of version 2009, RedPrairie uses Java to provide RF forms. The underlying engine is called "MTF" (Mobile Terminal Framework). These applications are VT based applications that may be accessed via telnet or ssh protocols.
Deployment Architecture
- A MOCA task exists for each vendor and warehouse combination. The task listens on a specific port and for a specific protocol (i.e. telnet or ssh). One task serves a single vendor (e.g. LXE, Intermac, or Default) and a specific warehouse
- The task also points to a MOCA connection URL.
- Clients connect to the specific task using ssh or telnet protocol
- If the vendor setup is defined correctly, the form will go directly to the login prompt and device identifier (devcod) will be determined based on RF Vendor setup. If not defined (e.g. DEFAULT vendor), then system will prompt for a device. This device is determined based on setup in "RF Terminal Maintenance"
Software Layout
"MTF" is a separate software component similar to MOCA, MCS, SAL, DCS, etc. and exists at the same directory level. This component is the underlying engine that runs MTF.
The actual forms are provided by the component that uses it, which by default is "DCS" and when we have some customized forms then also "LES".
Within each component that uses MTF, the objects exist in mtfclient/ sub-folder. The whole solution is compiled using "ant" which works off a file called "build.xml" in $LESDIR.
Within LES folder, the customized objects will exist in "mtfclient\src\java\com\redprairie\les" sub-folder and in that folder you can have one or two sub-folders that represents the customized objects:
- formlogic
- mtfutil
formlogic folder has the forms and mtfutil is needed for common classes.
RF Form Invocation Mechanism
When MTF calls a form, it uses the "createFormLogic" method, e.g:
As you can see we are calling it by a name and not invoking the java class directly. This setup is in rf_frm_mst table:
The custom entries have cust_lvl of 10 and the frm_cls points to the actual class path. You can see how this mechanism allows for overriding standard forms as well:
Creating a new RF Form
So creating a new RF form is quite straight forward, given that you know the underlying mechanism for creating the required java objects. You will put the new form in the formlogic folder and compile the code. You will put that in the desired menu and give the permissions to the appropriate roles. When MTF needs to invoke the form, it will use the rf_frm_mst data to find the highest customization level and instantiate that class.
Overriding Standard Forms
This is the main reason for this blog. I have seen far too many cases where the override is done by straight copy/paste of standard code and then modifying a few lines of code for the enhancement. Anyone who understands basic OOP principles will get a heart burn by reading this but unfortunately far too much code exists like that. Just look in your installation in this folder and see code like "PickupA.java", "DepositA.java", etc. and if that code is based on straight copy/paste of standard code - you are a victim of this practice. This basically implies that in case of a version upgrade, java is not going to help - you will have to retrofit these forms and somehow obtain the new source code.
Overriding Standard Forms - The Right Way
There is no advantage of using Java in MTF if the language is not utilized fully. Sure, java syntax is more mature than old RF forms but if you see the code there is really one to one correspondence - actually RedPrairie provides a converter that can take the old "rdt" files and convert them to "java" files.
The advantage is only if "java" is utilized fully. As always, the RedPrairie architecture is not at fault. Just like MOCA, MTF framework supports the "right way".
Lets say all you want to do is that when "DepositA" is invoked, it should do something different in "formEntry" than what it does right now. You can see what it does right now by looking at MTF and MOCA traces.
So the right approach will be to do just that and nothing more:
Here "formEntry" and "formExit" represent overridden methods that can have any code that you want.
- And then add an entry to "rf_frm_mst" to call "UsrDepostA" when DEPOSIT_A is called.
Overriding Methods In CWmdMtfUtil
This class is provided by DCS component and it contains several utility methods that are called by various forms. For example when directed work form is invoked, it calls "getWork" method from this class.
This class does not have a simple mechanism like forms, i.e. there is no table where we can put our class path so that RedPrairie invokes that instead of this class so this is a bit tricky!
When a form needs to call a method from this, it will have code like:
So what we want to do is put our extended class in this session map. If we can do that, the concepts described above would work. So our approach will be:
- And then find a suitable point where we can replace the session map with our object. A reasonable location may be the undirected menu. Undirected menu will be overridden using technique described above.
And thats it - now when we go to the undirected menu, after displaying it, it will replace this session variable and from that point on our override method will be invoked.
Conclusion
Cut/paste approach is often employed when modifying the Redprairie code. This problem exists at all levels - for example the "libsrc\varint" folder sometimes contains large C files with names like "trnAlloc.c" - that is a huge concern because that is core of RedPrairie and if that is customized say good bye to easy upgrades.
Same issue manifests at RDT level and GUI level. Even though RedPrairie architecture is almost perfect to support extensions, implementations are often sub-par. Customers should closely watch what objects are part of the rollouts they receive and if standard objects are being modified a red flag should be raised right there.
We have been able to successfully implement complicated enhancements without touching the underlying standard code. There could be situations where the only solution is to modify the base code. In those situations it is better for the customer to give up on the enhancement or come up with some compromise where the base code does not need to be touched.