Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 12 Current »

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.

Loading beans conditionally

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(openmrsVersion = "1.9.8 - 1.9.*"). @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 DrugOrder.getFrequency().

Create a similar class e.g. DrugOrderCompatibility1_10 implementing 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 DrugOrderCompatibility1_10.

Note that @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 @OpenmrsProfile and @Component. See https://github.com/openmrs/openmrs-module-htmlformentry/blob/master/api/src/main/java/org/openmrs/module/htmlformentry/handler/DrugOrderTagHandlerSupport1_6.java 

For example on OpenMRS 1.6.5 the @OpenmrsProfile annotation will be completely ignored, but @Component will not so the bean will be created.
For example on OpenMRS 1.6.5 the @OpenmrsProfile 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

For a full example see: https://tickets.openmrs.org/browse/HTML-515

Loading resources conditionally

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:

<conditionalResources>
	<conditionalResource>
		<path>/lib/yourmodule-api-1.10.*</path>
		<openmrsVersion>1.10</openmrsVersion>
	</conditionalResource>
</conditionalResources>

, 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.

Conditional loading based on running modules

It is also possible to load beans/resources based on running modules. Use @OpenmrsProfile(modules = {"metadatasharing:1.*", "metadatamapping:1.*"}) to load a bean only if the metadatasharing and the metadatamapping modules in version 1.x are running.

Similarly use

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

to conditionally load a resource.

Optionally you can specify OpenMRS version.

The feature was implemented in https://tickets.openmrs.org/browse/TRUNK-3644

  • No labels