In OpenMRS 1.10, 1.9.8, 1.8.5, 1.7.5 and later we introduced new ways to make it easier to develop modules supporting OpenMRS versions with incompatible API changes.
Let's consider changes in DrugOrder introduced in OpenMRS 1.10. In OpenMRS 1.9
DrugOrder.getFrequency() returns a string, whereas in OpenMRS 1.10 it returns a newly introduced
OrderFrequency object. It is a backwards incompatible change, which you need to consider writing a module that supports OpenMRS 1.10 and 1.9.
One way to approach such incompatible changes is to use Java Reflection API to invoke methods found with
java.lang.Class.getMethod(). It's considerably easy to do when all that changed is a method name, but gets quite complex when you have to deal with a newly introduced class.
The other approach that we suggest now is to use conditionally loaded beans or resources. We will explain how to use conditionally loaded beans first.
We will continue with the
DrugOrder example. You start from creating a maven submodule for a version of OpenMRS that has an incompatible change in API. In that submodule you need to set openmrs-api version to 1.10 in order to be able to use the
OrderFrequency class. Our suggested convention for such a submodule artifact id is yourmodule-api-1.10. Configure it to depend on yourmodule-api as well.
Next you have to refactor code that do any calls to
DrugOrder.getFrequency() by extracting it to a separate class e.g.
DrugOrderCompatibility1_9 which you will annotate with
@OpenmrsProfile instructs Spring to create a bean only when running OpenMRS in version from 1.9.8 (including) to 1.10 (excluding). Create an interface for that class e.g.
DrugOrderCompatibility and use it with
@Autowired in all places that used to call
Create a similar class e.g.
DrugOrderCompatibility in the 1.10 maven submodule. Annotate it with
@OpenmrsProfile(openmrsVersion = "1.10"), which will instruct Spring to create such a bean only when running OpenMRS 1.10 and later.
That's it! From now on if you install a module on OpenMRS 1.9.8 Spring will inject
DrugOrderComaptibility1_9, whereas on OpenMRS 1.10 it will inject
@OpenmrsProfile can be used even before OpenMRS 1.9.8, 1.8.5, 1.7.5 (before the feature was added). This annotation will be simply ignored in older versions of OpenMRS and Spring will not create beans for classes annotated with just
@OpenmrsProfile. In order for Spring to load a bean on versions of OpenMRS, which do not support conditional loading of beans you just need to annotate a class with both
@Component. See here in htmlformentry's
@OpenmrsProfileannotation will be completely ignored, but
@Componentwill not so the bean will be created.
@OpenmrsProfileonly. See here in htmlformentry's
@OpenmrsProfileannotation will be completely ignored and the bean will not be created.
For a full example see: https://tickets.openmrs.org/browse/HTML-515
Alternately or in addition to the above you can instruct the OpenMRS module loader to skip loading resources based on the OpenMRS version. After you create a maven submodule for a specific OpenMRS version, maven packages it in a jar file and puts in your omod in the lib directory. In config.xml in your module you can declare:
, which will instruct OpenMRS to load yourmodule-api-1.10.* only when OpenMRS version is 1.10 or above.
This is also a handy feature if your module uses a library that you ship with your module, but it is now included in OpenMRS core. You want to load such a library only on OpenMRS versions that don't provide it.
It is also possible to load beans/resources based on running modules. For instance below is the annotation to add to load a bean only if the metadatasharing and the metadatamapping modules in version 1.x are running:
to conditionally load a resource.
Optionally you can specify OpenMRS version.
The feature was implemented in https://tickets.openmrs.org/browse/TRUNK-3644
As of OpenMRS 2.2.0 it is possible to control loading beans on the condition that modules are missing. This is how a bean is loaded under the condition that the module exti18n is missing:
Alternatively this can also be done via config.xml:
Note that in this case it is important to not complement the use of
@Component (or a bean definition in moduleApplicationContext.xml). This is because the bean must be filtered out well ahead of everything else, which would be defeated by Spring processing the