Logging my experiences while coding

Develop a modular application – Loading classes using ClassLoader

In Java, all the classes are loaded using several ClassLoader. In this article, we’ll develop a loader for our modules and watch the problems that arrive when working with custom ClassLoaders.

Normally, Java use the system ClassLoader to load all the classes of our application. So it contains all the classes of our application and all the classes our application needs to work. But the problem is that we cannot add our modules jar files into classpath because the application doesn’t know the modules jar files names.

Moreover, we cannot theoretically add files to the system ClassLoader. I say theoretically because, we can add files using reflection and call to a private method, but I think it’s not a really good practice.

So we’ve to create a new ClassLoader to load our modules. We’ll do that in two phases :

Browse the module files to get the classes of the modules and the URLs of the modules Jar files

Load the modules into our ClassLoader using the URLs of the first phase

We’ll do all the loading in a new class ModularLoader. so let’s create a create a method that return the list of classes to load:

publicclassModuleLoader{privatestaticList<URL>urls=newArrayList<URL>();privatestaticList<String>getModuleClasses(){List<String>classes=newArrayList<String>();//Get all the modules of the modules folderFile[]files=newFile("folder").listFiles(newModuleFilter());for(Filef:files){JarFilejarFile=null;try{//Open the Jar FilejarFile=newJarFile(f);//We get the manifestManifestmanifest=jarFile.getManifest();//We get the class name from the manifest attributesclasses.add(manifest.getMainAttributes().getValue("Module-Class"));urls.add(f.toURI().toURL());}catch(IOExceptione){e.printStackTrace();}finally{if(jarFile!=null){try{jarFile.close();}catch(IOExceptione){e.printStackTrace();}}}}returnclasses;}privatestaticclassModuleFilterimplementsFileFilter{@Overridepublicbooleanaccept(Filefile){returnfile.isFile()&amp;&amp;file.getName().toLowerCase().endsWith(".jar");}}}

Like you see, it’s not complicated at all. We search all the module files and then for each jar file, we open it, get the manifest et read the class name of the module. And then, for the second phase, we get the URL to the Jar file.

Of course, this loader is not perfect. We can have modules with no manifest or manifest with no class name and the errors must be correctly treated, but this is not the objective of this post to be perfect.

Now we can do the second phase, adding a method to create the ClassLoader, instantiate the modules and return them :

privatestaticClassLoaderclassLoader;publicstaticList<IModule>loadModules(){List<IModule>modules=newArrayList<IModule>();AccessController.doPrivileged(newPrivilegedAction<Object>(){@OverridepublicObjectrun(){classLoader=newURLClassLoader(urls.toArray(newURL[urls.size()]),ModuleLoader.class.getClassLoader());returnnull;}});//Load all the modulesfor(Stringc:getModuleClasses()){try{Class<?>moduleClass=Class.forName(c,true,classLoader);if(IModule.class.isAssignableFrom(moduleClass)){Class<IModule>castedClass=(Class<IModule>)moduleClass;IModulemodule=castedClass.newInstance();modules.add(module);}}catch(ClassNotFoundExceptione){e.printStackTrace();}catch(InstantiationExceptione){e.printStackTrace();}catch(IllegalAccessExceptione){e.printStackTrace();}}returnmodules;}

So we start creating a new ClassLoader taking the urls of the Jar files. Then, we use this ClassLoader to load all the module classes and instantiate them. We only verify if the class is of type IModule.

This is all for our ModuleLoader. We can now test our simple modular application. We create a JAR file for the module of the previous post and then we create a very simple application to test that :

List<IModule>modules=ModuleLoader.loadModules();for(IModulemodule:modules){System.out.println("Plug : "+module.getName());module.plug();}System.out.println("Lot of other things done by the application. ");for(IModulemodule:modules){module.unplug();}

Like you can see, we just created a modular applications! The application doesn’t know the modules, but the modules can do things in the application.

Of course, to create a real application, we have to develop all the extension points and services, but this is a base to start with.

However, there is some problems with the current implementations :

We cannot deploy modules without restarting the application, because we must create a new ClassLoader for the modules. This is possible if there is no interation between modules, but that’s not often the case. You have also the possibility to isolate all the modules in a specific ClassLoader, but with that second solution, the interations between modules are made harder.

Using a second ClassLoader may be problematic with libraries loading dynamically the classes like Spring or Hibernate. To make these libraries working with your ClassLoader, you have to look at case by case depending on the library. Often, you achieve specifying the contextClassLoader using the method Thread.currentThread().setContextClassLoader(ClassLoader cl) with your ClassLoader

So here is the end of this four posts about creating a modular application. I hope you find these posts interesting.