A work around on legacy code

Domain specific standard api.

The company I worked for offer Software As A Service to various company in the same industry. The system is old enough to accumulate tons of technical debts.
One of the most noticeable problem was the control of the software specification. For some reason, the system shared single code base yet all company do have their own custom specification. The way they handle this sort of requirement is as follow:

if(company.equals("companyA"){
    // do stuff specifically for company 'A'
}

As the result, the current code base is virtually impossible to maintain. The only benefit gain from this approach is that any change request by a particular company can be easily done in short amount of time.

Recently, I am assigned to write some web service to fulfill the new requirement launching this July. With the new requirement, the work flow of this particular domain is completely change and hence legacy code base reuse is not that simple (not to mention the code base is tightly couple with the runtime context e.g. HttpSession in servlet-api)

I face quite a lot of trouble when dealing with the old code base and decided to rewrite some core component with reuse-ability in mind. Since the application is written entirely with java + jsp (and run on TomCat 6). I ported the application to TomEE 1.7.4. (which require an update of jdk to version 1.6).

Context Dependency Inject (JSR-330) is the key to separation of concern. The way Java EE platform is pretty much what I want to do:

  1. Define a set of standard API and all the program will only have reference to this set of API (typically an interface, annotation or some abstract class). The CDI container will then inject the implementation of the API base which is exist in the classpath. (e.g. TomEE Plus come with OpenJPA but the application don’t have to be aware of the OpenJPA classes)
  2. Implement the API and package it with different jar.
  3. Deploy the jar to the server.
  4. The implementation will be picked up automatically during execution. ( Here’s the true power of dynamic binding)

One thing to note is that Java handle classes in a weird way (this is valid up till JDK 1.8). Every classes is actually loaded in a global scope and when there’s 2 jar with same class name inside (fully qualified name) the jar file. The class in the later jar will be register to the class path.

And now finally come to the title. Domain specific standard pattern. Put it in simple term, I copy the concept of Java EE platform and bring it to application level. The known problem here is that we shared same code base for every single client and created a big mess. The only way to fix this is to separate every client to its own package. Do note that this system is written back in 1999 (almost 18 from now). It is not feasible to migrate every company to its own code base.

The keyword here is domain specific. The company I work for is writing software to the insurance industry. And most insurance company will shared same domain context, (e.g. both required some specific underwriting check/review before creating a transaction)

The api can be as simple as:

public interface QuotationService{
    public List<Quotation> findQuotationByID(String id);
}

So, we got our standard API now. Let’s implement it:

public class CompanyAQuotationService implements QuotationService{
    @Override
    public List<Quotation> findQuotationByID(){
        //query and return
    }

}

The above class is suppose to be packaged in different jar file and deploy with the api file.

When we talk about polymorphism, some people do :

//class definition omitted

QuotationService service = new CompanyAQuotationService();

This is not helping at all. Since we still have reference to CompanyAQuotationService class, we didn’t really solve the coupling problem. Besides, the application will even failed during deployment time whe there’s 2 implementation of QuotationService, note that I mention 2 implementation, not 2 same class with same fully qualified name.

//within class scope
@Inject
QuotationService service;

The code above will run without any issue under application server (With CDI container). One should use setter injection instead for the ease of unit test (Mocking the host in particular).

Now we have our implementation for company A. Let say company B have some change request on quotation service.
We can simply write another implementation to do perform the customization.

public CompanyBQuotationService implements QuotationService{
     @Override
    public List<Quotation> findQuotationByID(){
        //query and return
    }
}

Note: the code above isn’t going to work if there’s 2 implementation in same classpath.

Dynamically inject the implementation

Now we introduce a qualifier annotation

@Qualifier
@Target({TYPE,METHOD})
@Retention(RUNTIME)
public @interface Company{
    String value();
}

A qualifier annotation is just a marker or id to allow cdi do more advance lookup during injection. Fortunately this is only required once.

@Company("A")
class CompanyAQuotationService implements QuotationService{
}

//another class
@Company("B")
class CompanyBQuotationService implements QuotationService{
}
//the executing class
@Inject
@Company("B")
private QuotationService service;//the company b instance will be injected here

This sort of implementation some what fit my needs. But still, there’s a need to specify which company to inject. Most of the time the application context will do checking on a global variable company, and customize system behavior base on this variable. What I want to achieve is as follow:

@Inject
SomeInjector injector;

QuotationService service=  injector.getInstanceOf(company);

And I found this answer on StackOverflow.

public class CompanyImpl implements Company extends AnnotationLiteral<Company>{
    public CompanyImpl(String value){
        this.value = value;
}
}
//The injector/factory

@Inject
@Any
Instance<QuotationService> quotationServicePool;// instance isn't really pool, but a pool of implementation seems logical

public QuotationService inject(String company) throws Exception{
    CompanyImpl impl =new CompanyImpl(company);
    return quotationServicePool.select(impl);
}

OK, this fulfilled my requirement on runtime injection. But this also introduce another problem, I have to write an injector for every single standard API.

My recent research found that Xtend’s active annotation might solve my problem.

P/S:

  1. I don’t think it is feasible to start a project like this (multiple company sharing same source), but I am working on a legacy project and have very limited choice. I assume this sort of design will consume relatively higher memory.
  2. Every single implementation jar file must have an empty beans.xml file in META-INF/ folder and WEB-INF folder in the web applications (true for Java EE 6, it’s enable by default in version 7).

References

Context Dependency Injection
AnnotationLiteral

Advertisements