Last week I had a hard time with several dependency issues.
The hardest one appeared when I tried to integrate optaplanner into openmrs. Optaplanner is a large library with several transitive dependencies. In the end I found out that some classes weren’t loaded from ecj-3.7.2.jar, but from core-3.1.1.jar. The first resides in my modules lib folder the latter is an outdated library imported from maven jetty plugin.
I had another problem with jodatime and found out that the modules that are packaged with openmrs-distro-referenceapplication use three different versions of this library:
- Htmlformentry: 2.1
- emr and others: 2.2
- reporting: 2.3
In my case the classloader sometimes decided to load DateTime from emrapi regardless if the library was present in my lib folder or not. I found the following ticket that describes this problem:
In a comment @bwolfe described the classloading process:
- Module asks OpenmrsClassLoader for class string X
- If OCL has loaded class X already, returns class X
- If OC has not loaded class X, look in ModuleClassLoader for requesting module (and its jars).
- If not found, look in ‘required modules’ of requesting module class
At the moment it is a bit like using global variables. You can’t be sure that the classes during testing are the same that are used during runtime. It depends on the loaded modules.
Before I continue I want to say that I am not an expert on class loading, but I wanted to discuss possible solutions with you.
From my point of view dependency issues can be divided into the following groups:
- make sure that related modules are compiled, tested and run with the same versions.
- make sure that module dependencies on other libraries are independent from each other
@ 1 This problem already broke the install process of the reference application - RA-331: The referencemetadata module was compiled and tested against idgen 2.7, but was distributed with idgen 2.8-SNAPSHOT. A message signature changed causing an exception at runtime, although all tests passed during compilation.
Suggestion: Create a common parent pom that defines all version numbers for each module. Let all modules derive from this file.
Benefit: Increased trust, that the application won’t break if I use the exact same module versions.
@ 2 In this case we have to distinguish between:
- entities that are private to this module
- and those that are shared between modules
I think the first one could be solved by using independent class loaders, that don’t load classes from other module libraries. Otherwise a single dependency in some other module can break the application.
The second one could be solved in a way that the library is added to the common parent pom. This means that modules are compatible with each other because they include the same version of this dependency into their private classpath.
The reference application already consists of several modules. This number will certainly rise in future. In order to ensure high quality and maintainability we have to discuss this issue.
I hope I could describe my concerns in an understandable way – otherwise I am happy to clarify!
I am looking forward to hear your opinions!