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 returns a string, whereas in OpenMRS 1.10 it returns a newly introduced OrderFrequency
object 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.
...
We will continue with the DrugOrder
example 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 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 by extracting it to a separate class e.g. DrugOrderCompatibility1_9
which which you will annotate with
Code Block | ||
---|---|---|
| ||
@OpenmrsProfile(openmrsVersion = "1.9.8 - 1.9.*") |
...
@OpenmrsProfile
instructs 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 and use it with @Autowired in with @Autowired
in all places that used to call DrugOrder.getFrequency()
.
Create a similar class e.g. DrugOrderCompatibility1_10
implementing implementing DrugOrderCompatibility
in 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 DrugOrderCompatibility1_10
.
Note that @OpenmrsProfile
can 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 @OpenmrsProfile
and and @Component
. See https://github.com/openmrs/openmrs-module-htmlformentry/blob/master/api/src/main/java/org/openmrs/module/htmlformentry/handler/here in htmlformentry's DrugOrderTagHandlerSupport1_6
.java
@OpenmrsProfile
annotation annotation will be completely ignored, but @Component
will will not so the bean will be created.@OpenmrsProfile
only only. See https://github.com/openmrs/openmrs-module-htmlformentry/blob/master/api-1.10/src/main/java/org/openmrs/module/htmlformentry/handler/here in htmlformentry's DrugOrderTagHandlerSupport1_10
.java@OpenmrsProfile
annotation annotation will be completely ignored and the bean will not be created....
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 . Use @OpenmrsProfilein version 1.x are running:
Code Block | ||
---|---|---|
| ||
@OpenmrsProfile(modules = {"metadatasharing:1.*", "metadatamapping:1.*"}) |
...
Similarly use
Code Block |
---|
<conditionalResources> <conditionalResource> <path>/lib/yourmodule-api-1.10.*</path> <modules> <module> <moduleId>metadatasharing</moduleId> <version>1.*</version> </module> <module> <moduleId>metadatamapping</moduleId> <version>1.*</version> </module> </modules> </conditionalResource> </conditionalResources> |
...
The feature was implemented in https://tickets.openmrs.org/browse/TRUNK-3644
Conditional loading based on missing modules
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:
Code Block | ||
---|---|---|
| ||
@OpenmrsProfile(modules = {"!exti18n"}) |
Alternatively this can also be done via config.xml:
Code Block |
---|
<conditionalResources>
<conditionalResource>
<path>/lib/yourmodule-api-*</path>
<modules>
<module>
<moduleId>exti18n</moduleId>
<version>!</version>
</module>
</modules>
</conditionalResource>
</conditionalResources> |
Note that in this case it is important to not complement the use of @OpenmrsProfile
and @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 @Component
annotation.
The feature was implemented in https://tickets.openmrs.org/browse/TRUNK-5213