Friday, November 13, 2009

Customization and Extension Architecture for Java Web Applications

It is a must that every ERP package has customization architecture. ERP packages are modified according to special requirements of many customers. So we needed customization architecture for our own ERP (composed of many Java EE web applications) too. I analyzed existing ERP packages(i.e. MS Dynamics Customization Architecture) to find out how we can develop such a system for Java EE web applications. There is no real clue from their architecture because of both their programming environment and architecture. I tried to analyze some extension mechanisms within some well-known Java applications like Eclipse. Eclipse plug-in system is great but it doesn’t suit Java web application extension requirements. I read many articles from Internet but there is no satisfactory answer for the problem.

After many years of thinking this issue, I found the answer. Answer was surprising because it was so close to me that I couldn’t see it; Inheritance and Dependency Injection(some may argue that this is Service Locator pattern but I preferred this term). Here in this post, I’ll present how to use inheritance (thanks to this excellent OOP feature of Java) and DI for application customization and extension requirements.



CUSTOMIZATION TYPES

Throughout this article I’ll use one term “Customization” to mean extension, plug-in or add-on etc. Let’s start with the categorization of customizations. In a typical application, there are 5 types of customizations (Cost increases from top to bottom):

1- By Data (Configuration with Parameters)
The cheapest solution for customization and can be done via system parameter files, or parameter tables.

2- By Workflow and Rule Engine
These systems provide semi-programmatic dynamic logic and can be changed without compilation in the current system on-the-fly. These solutions are cheap compared with programming customizations but it still requires some technical efforts.

3- By Addition
It means adding new applications to the system. To add a new application to existing Enterprise Application is the easiest programmatic customization since it is independent of current applications. In this method, programmer who makes addition may need to use your system libraries. When installing new versions of core applications, binary compatibility should be tested if any library of applications is used.

4- By Extension
Existing core applications functions are modified to meet new requirements without touching the base code. In this method, programmer would need to link base elements in his IDE (otherwise extension may not be compiled). We could support extension for Persistence Objects, Servlets, JSPs, Reports, JavaScript library, Framework and Business Logic. Every component type is extended with different methods, I’ll mention below. In new core application versions, both binary and functional compatibility should be tested.

5- By Modification
In this type, existing core application codes are changed. Programmer would probably need source code of applications (JSPs or JavaScript codes are already open source if not obfuscated). In new application versions, modifications would be lost. Programmer has to apply all modifications in every related new version. This is the most costly customization option.



EXTENSION OF PROGRAMMATIC ELEMENTS

In extension customization type, I said that every programming element can be extended. Here is the complete list of techniques we used for extension:

Persistence Objects
You need 2 things to achieve extension of persistence objects; firstly your persistence object can be extended. Yes, using built-in Java “extends” keyword is not enough so the problem is that does the extended persistence objects work in your Persistence Framework? Second requirement is that you should have a Factory class to return extended object in the run-time. You should build an extension lookup table to understand if this object is extended. If it is extended Factory creates extended object and returns it (DI). In this way, you can change functions of persistence objects by overriding its methods.

//in run-time DBSalesOrderExtension object is created
DBSalesOrder dbSalesOrder = BOFactory.get(DBSalesOrder.class);

public class DBSalesOrderExtension extends DBSalesOrder{
...
}

Servlets
Again you need 2 things to achieve this; firstly there should be no limitation to use extended Servlets in your web framework. Secondly you can forward a request to extended Servlet in “Front Controller” code. Since servlets are created by “Servlet Engines”, you can’t use Factory like persistence objects. Again, you should have a customization definition table to look up extended servlets. Your “Front Controller” checks this lookup table if current Servlet is extended. If so, it forwards, if not it continues processing. Client code or server configurations don’t need any adjustments.

//In Front Controller:
public final void service(HttpServletRequest request, HttpServletResponse response){
...
String extendedSalesOrderServlet = getExtensionName(salesOrderServlet);
if(extendedSalesOrderServlet != null){
doForward(extendedSalesOrderServlet);
return;
}
...
}

public class SalesOrderServlet extends HttpServlet{
...
public void doGet(HttpServletRequest request, HttpServletResponse response){...}
public void doPost(HttpServletRequest request, HttpServletResponse response){...}
}

public class ExtendedSalesOrderServlet extends SalesOrderServlet{
...
public void doGet(HttpServletRequest request, HttpServletResponse response){...}
public void doPost(HttpServletRequest request, HttpServletResponse response){...}
}

JSPs
For JSP extension, you need only one thing; you should have JSP access method(s) to open/redirect JSP pages so that we can inject extended JSP name in these methods. In practice, JSPs can’t be extended with Java’s inheritance mechanism. You can only extend it by copying an existing JSP and modifying its content. Again, you should define its base and extended name within customization lookup table. In JSP access method, you check if target (base) JSP is extended. If extended you redirect to the customized JSP page.

//Original Page
SalesOrder.jsp

//Extended Page
ExtendedSalesOrder.jsp

//In this method, "ExtendedSalesOrder.jsp" is displayed
openPage("SalesOrder.jsp")

Reports
Like JSPs, reports can be extended by copying and modifying it (This is a general approach (I remember same approach within Oracle ADF), if a software element includes GUI elements, inheritance becomes useless for extension). We have company association of reports so we didn’t need a mechanism for extended reports. It is just defined as this company uses this report and company-specific report is executed when requested.

JavaScript Library
We created an empty extension file (js_extension.js) and include it in every JSP page so that JavaScript methods can me modified. In our JS library, everything is method. In JS, there is no direct support for inheritance and overriding. Extender programmer can remove a method in base JavaScript file and add its changed version to this extension file (Overriding or overloading is not possible in JavaScript) or add a wrapper function with different name and use it in his web applications. You can develop a better extension than mentioned here by simulating inheritance in JavaScript.

Framework
Some services can be extended by making new implementations. Here, DI is leveraged again. In our frameworks, we make extension possible in some functions. Implementation is defined within framework configuration file and that implementation is loaded during framework initialization.

public class MyFrameworkSystem{
private IFrameworkService frameworkService;
...
public static void initialize(){
frameworkService = loadImplementation(getImplementationClassNameFromConfFile("FrameworkService"));
...
}
}

public interface IFrameworkService{
public void extensibleMethod();
}

Business Logic
If you need only a fragment of code change or a control is used in many places in base code which are hard to locate by extenders, what do you do? By default, you can extend that object and override it. If that method is a big method, you have to copy all lines to new method and change a piece of it. Maintenance would be a nightmare in that case. A neater solution is to develop an extension injection mechanism. We developed “Extension Points” for just that requirement. If some business logic in base code is going to be modified, we can take it to an extension class to easily override it. In that way, extension can be plugged to any piece of business logic. How can we know which part is customized can’t be answered at the moment. That would be shaped by projects and core customization requirements. Again “Extension Points” should be defined in your customization lookup table. (One interesting case is that you may want to change business logic any time without compilation. Here I mentioned about Rule Engines, you can define a rule and call it in your program. In that way, you can change that rule any time you want; “Dynamic Business Logic”)

void salesOrderBusinessLogic(){
...
SalesOrderBusinessLogic soBL = (SalesOrderBusinessLogic) getExtensionPoint(SalesOrderBusinessLogic.class);
if(soBL.businessLogic())
...
}

public class SalesOrderBusinessLogic implements IExtensionPoint{
public boolean businessLogic(){
//some business logic here
}
}

public class ExtendedSalesOrderBusinessLogic extends SalesOrderBusinessLogic {
public boolean businessLogic(){
//some modified business logic here
}
}



USER CUSTOMIZATION

When talking about application customization, generally user customization is understood. In this sense, you can develop customization options for your applications. You can provide GUI-level user customization facilities. Let’s briefly see what kind of user customizations can be developed:

List Customization
HTML list customization is interestingly difficult (To see that try to change column orders). You may enable your users to customize lists; show/hide columns, column orders, column widths etc.

Input Form Customization
Some user fields may be added to forms by your users. Let’s say your user needs an extra column to track something, if you provide extra column feature (i.e. predefined user columns) they can easily add new fields. Or users may need some default values. For example you can enable them to get last entered data to a form input element. When they re-enter a form and press a shortcut key, their last entered data may be restored to that field again without requiring filling that data.

Report Customization
Your users may need to hide/show some columns etc. Or they may need to add new criteria to report parameter input page. Or they may want to change sort or group columns.

1 comment:

Lydia Justin said...

I would like to say one good thing regarding QA testing
Load & performance testing for ERP application by using our software testing service
Indium software provides Outsourced and In-housing Manual & Regression Test Automation Testing Services across the world. Testing & Enhancing the performance of your ERP mobile or web application under load and stress. We have a strong expertise in cloud based application testing, Game & Mobile testing, Social-Digital media testing and Enterprise apps testing.