Vision Statement

This project was inspired by the requirement of loading Java dependencies at run time with the ability to dynamically switch dependency versions without application re-start. This can be useful for a Java library that can be downloaded and invoked while older versions of the library still present and can be used.

Use Case

Let’s imagine a front end application that invokes back end Java library for processing and needs to be able to switch between processor versions without application re-start. Moreover, this application will be able to download new versions of the processor library and make it available without any re-installer. In addition, you need iterating over its design or implement features that a group of your customers is interested in. Some of the features may affect results of the processing that are not desirable for the other group of customers. Your product can offer a solution that can satisfy both groups by providing in-process switch for the version of your processing engine. In addition, the other group of customers can try-out new version of the engine and have some time to adopt to new results.

Other Solutions

Although, I think that the itzap-proxy is great there are other solutions that can implement this requirement, or get close to it.

JBoss Modules

Jboss Modules http://jboss-modules.github.io/jboss-modules/manual/ can be used to implement this requirement. They provide an isolated class loader that can be used to load different versions of the processing engine Java back end.

Java CGI

Java CGI is originally intended for this kind of scenarios. https://www.javaworld.com/article/2076863/write-cgi-programs-in-java.html

Why itzap-proxy

The main advantage of the itzap-proxy is the ease of use. While JBoss modules can solve the problem of class loading isolation, they impose some requirement on how modules have to be defined. To effectively use JBoss Modules Java engine has to be originally designed with JBoss modules in mind which makes it impossible to retrofit older versions of the Java engine that customers may still want to use with your application. itzap-proxy does not impose any changes on the Java library that needs to be loaded at run time, which makes it a perfect solution in situations when older versions of the engine still need to be used, while new versions are being rolled out. Java CGI, on the other hand, is hard. There are frameworks that make it easier to use, but it makes the application more heavyweight.

Other Applications

Isolated Class Loader

itzap-proxy provides isolated class loader that can be used to load dependencies. If there are conflicting versions of libraries used by dependencies that cannot be reconciled by maven excludes itzap-proxy provides a way to load them using dedicated class loader. Most common examples are log4j or Apache POI used in the third party libraries.

Mixing Java 7 and Java 8

Suppose, your application provides functionality that targets different user groups, and one group cannot upgrade JVM to Java 8 while another group needs features of Java 8. Using itzap-proxy you can combine Java 7 application with Java 8 dependencies that are loaded at run time by the “Java 8 user group”. This way, you can still maintain single application with features supported by Java 7 and Java 8.

Usage

itzap-proxy

Load java jar files dynamically in isolated class loader to avoid dependency conflicts and enable modular updates. All jar files in the [main jar folder]/lib/myLib/2.0/*.jar will be loaded.

Visit my ITZap blog to read more about this project.

Code Examples

  1. Create Object from a JAR file located in the myLib/2.0 folder:
ProxyCallerInterface object = ObjectBuilder.builder()
       .setPackageName("org.my.package")
       .setClassName("MyClass")
       .setVersionInfo(newVersionInfo("myLib", "2.0"))
       .build();
object.call("objectMethod");
  1. Create object from factory method and pass "string param" and 1 to it:
ProxyCallerInterface object = ObjectBuilder.builder()
       .setPackageName("org.my.package")
       .setClassName("MyClass")
       .setVersionInfo(newVersionInfo("myLib", "2.0"))
       .setFactoryMethod("initMyClass")
       .setParams("string param", 1)
       .build();
  1. Object builder can implement proxy interface:
ProxyCallerInterface object = ObjectBuilder.builder()
   .setClassName("org.mypackage.myclass")
   .setVersionInfo(newVersionInfo("myLib", "2.0"))
   .build();

// Example with call back
object.call("addListener", ObjectBuilder.from(builder)
                            .setInterfaceName("org.mypackage.MyListener")
                            .setHandler(new InvocationHandler() {
                                @Override
                                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                   // handle call back
                                   return null;
                                })
                            .build()
  1. Instantiating object with constructor parameters
ProxyCallerInterface object = ObjectBuilder.builder()
       .setPackageName("org.my.package")
       .setClassName("MyClass")
       .setVersionInfo(newVersionInfo("myLib", "2.0"))
       .setParams("String param", 1)
       .build();
       
  1. Loading Enums
Map<String, ProxyEnum> enum = ObjectBuilder.from(builder)
                                    .setClassName("MyClass$USStates")
                                    .buildEnum();
  1. Calling setters on a created object
List<MethodDesriptor> descriptors = Lists.newArrayList();
ObjectBuilder builder = ObjectBuilder.builder()
                .setClassName("org.mypackage.MyClass")
                .setParams("String param")
                .setDescriptors(descriptors);
// call setter
descriptors.add(MethodDesriptor.method("setProperty", "property name", "value"));
ProxyCallerInterface object = builder.build();
  1. Calling static methods
List<MethodDesriptor> settings = Lists.newArrayList();
ObjectBuilder builder = ObjectBuilder.from(builder)
                .setClassName("org.MyUtils")
                .setStaticObject(true)
                .setDescriptors(settings);
                
settings.add(MethodDesriptor.method("convert", "from this string"));
builder.build();
 

Leave a Reply

Your email address will not be published. Required fields are marked *

2 replies
  • I simply wished to thank you very much yet again. I am not sure the things that I would have implemented without the entire tips shown by you over such theme. It was the horrifying scenario for me, nevertheless noticing the very expert technique you dealt with that forced me to jump for contentment. I’m just happier for your assistance and as well , expect you know what a great job your are putting in training people today using your webpage. I am certain you’ve never come across all of us.

  • I wanted to put you this very little remark just to give many thanks once again about the striking concepts you have featured here. This is incredibly open-handed of you to deliver without restraint all that a number of us would have made available for an e book to earn some dough for themselves, mostly given that you might well have done it if you decided. Those basics in addition worked as a easy way to realize that other people online have a similar interest just as mine to understand a good deal more regarding this problem. I think there are lots of more pleasant instances in the future for those who take a look at your website.