Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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
languagejava
@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 

For In this example on OpenMRS 1.6.5 the @OpenmrsProfile annotation  annotation will be completely ignored, but @Component will  will not so the bean will be created.
For any classes that need to be loaded conditionally you use @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
For In this example on OpenMRS 1.6.5 the @OpenmrsProfile annotation  annotation will be completely ignored and the bean will not be created.
Using this approach we made the htmlformentry module work on OpenMRS versions that don't have conditional loading of resources

...

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
languagejava
@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
languagejava
@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