UI Framework: How does parameter class injection work in controller methods?

Application Name: Spring, UI Framework Version Number: Ref App 2.3+

Question: (1) In controller methods, what is the mechanism behind the injection of some classes based on an ID or a UUID? (2) How can I bring this to more classes?

Example:

A controller method where the patient’s UUID is provided as a request parameter "patient":

public void doSomething(@RequestParam("patient") Patient patient, ...) {
  ...
}

… allows to get the Patient instance injected straight away in the parameter-variable patient. Obviously Spring knows how to fetch the Patient instance from its UUID. But this presumably only works with some classes. Where/how has this been configured, and how can I bring this to new classes via my custom module?

1 Like

The underlying mechanism is Spring’s ConversionService.

See all the StringTo*Converter classes here: https://github.com/openmrs/openmrs-module-uiframework/blob/3.6/api/src/main/java/org/openmrs/ui/framework/converter

Support for uuids was added opportunistically to some converters that needed it, but many would be lacking it.

An example of doing it right is https://github.com/openmrs/openmrs-module-uiframework/blob/3.6/api/src/main/java/org/openmrs/ui/framework/converter/StringToVisitConverter.java

Go ahead and send PRs for any converters you want to improve, and I’m happy to do a new UI Framework release with these changes.

(Also you can define converters in your own module as Spring beans, though I don’t know offhand if you can override an existing one, via bean ordering or something.)

Ah, thanks! I was about to ping you :wink: Ok I’ll dig into the resources you pointed out and I will come back to this thread later.

Hi @darius, seems that I forgot about this thread… I would like to revive it to ask a question about configuring Spring’s converters.

While I was working on making VDUI 1.10.x compatible, I just realised that in the Ref App 2.1 / Core 1.10.x environment the StringToProviderConverter is not picked up by Spring at runtime:

java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [org.openmrs.Provider]: no matching editors or conversion strategy found
  at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:231)
  at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93)
  at org.springframework.beans.SimpleTypeConverter.convertIfNecessary(SimpleTypeConverter.java:49)
  ...

It seems that, although this converter is provided by the UI Framework module, it is not added in its webModuleApplicationContext.xml. To be honest it is not added to this date either and yet this issue arises only with the Ref App 2.1, and I am not sure why this starts working with later versions… (such as Ref App 2.3).

Anyway so how can I notify Spring that I want the ConversionService to be aware of StringToProviderConverter?

I tried adding this VDUI’s webModuleApplicationContext.xml:

Attempt #1

<bean id="conversionService" class="org.openmrs.module.uiframework.UiFrameworkConversionServiceFactoryBean">
  <property name="converters">
    <list>
      <bean class="org.openmrs.ui.framework.converter.StringToProviderConverter" />
    </list>
  </property>
</bean>

Attempt #2 (more after the model of what’s done in the UI Framework module)

<bean class="org.openmrs.ui.framework.converter.StringToProviderConverter" />
<bean id="conversionService" class="org.openmrs.module.uiframework.UiFrameworkConversionServiceFactoryBean"/>

Neither worked, I keep getting the same stack trace regardless. Would you have any clues?

Offhand I have no idea.

The reason it’s not mentioned in webModuleApplicationContext.xml is that the bean is instantiated via an @Component annotation. (I think I was originally doing them via xml, but eventually we moved to annotations, and never changed the old ones.)

Could it be that because you’re in a RefApp environment, which has the providermanagement module, the Provider class in that module messes things up somehow?

Unfortunately I think I see some hours with a debugger in your future… (Can you replicate this in a unit test?)