Handler Overriding and Priority

                 

Working with complex Observations - how to define which handler should be loaded at initialization

 

Users have different requirements. Different implementations may have different complex data type handlers, and each handler may come in several varieties based on user preference.

                              

Why would users need several implementations of the same handler, and how to decide their priorities?

         

Let’s consider the following,

Currently the OpenMRS api contains PatientHandler.java. This represents the core / most basic level of how a user may successfully store / retrieve a complex obs for a patient.

When creating an Observation using a concept created with this handler, the user is provided a simple text box to input the patient id number. This id is used to retrieve the matching patient.

However assume that a second user is not satisfied by merely inputting a patient id as the obs value. This user prefers an auto complex box, where he can search for and select matching values.

             

To this end, the developer must do two things,

  1. Create a new PatientHandler class which extends the existing api level PatientHandler.
  2. Ensure that the @Order value of this class is higher than that assigned to the core handler.

                                    

In the case of PatientHandlers, this has already been done for you.

The overriding handler class in named patientFieldGenObshandler.java, and is located in the web module.

                                 

This class re-uses methods in the core PatientHandler, and introduces a single new method,

public String getWidgetName();

                 

This method lets the system display an auto complex box using fieldGen tags.

Users who wish to develop Handlers for data types already supported by fieldGen may do so using this design.

Users who wish to develop handlers for data types not supported by fieldGen tags may either first enhance fildGen to support this functionality, or else decide on an alternative approach based on their needs.

               

Domain data types currently supported by the fieldGen tag are,

●      Concepts

●      Dates

●      Patients

●      Persons

●      Encounters

●      Drugs

●      Users

●      Locations

●      OrderTypes

                          

However overriding handlers of the same type does not end here – developers may create an n number of Handlers based on their needs. Nor are there any constraint forcing developers to use only fieldGen tags to display their data. The Handler system is easily configurable to meet all demands.

Identifying which handler to load and evaluating their priority is done using Spring Components.

Spring: How priority is determined

                

Let’s assume that your system contains both PatientHandler and PatientFieldGenObshandler.

PatientFieldGenObshandler extends PatientHandler, and the HANDLER_TYPE constant of both are ‘PatientHandler’.

At spring initialization time, spring auto detects and loads all classes that implement the ComplexObsHandler interface. Thus, it identifies both above handlers, as well as others.

Priority is decided using the methods prioritizeHandlers() and getOrder()

     

/**

       * Prioritize handlers.

       */

      private synchronized void prioritizeHandlers() {

            if (prioritizedHandlerClasses == null) {

                  prioritizedHandlerClasses = new HashMap<String, Class<? extends ComplexObsHandler>>();

                  for (ComplexObsHandler handler : handlers) {

                        Class<? extends ComplexObsHandler> clazz = handler.getClass();

                        if (!prioritizedHandlerClasses.containsKey(handler.getHandlerType())) {

                              prioritizedHandlerClasses.put(handler.getHandlerType(), (Class<? extends ComplexObsHandler>) clazz);

                              log.info("Loaded :" + handler.getHandlerType());

                        } else {

                              int candidateOrder = getOrder((Class<? extends ComplexObsHandler>) clazz);

                              int existingOrder = getOrder(prioritizedHandlerClasses.get(handler.getHandlerType()));

                              if (candidateOrder < existingOrder) {

                                    prioritizedHandlerClasses.put(handler.getHandlerType(), (Class<? extends ComplexObsHandler>) clazz);

                                    log.info("Selecting Handler Based On Priority :" + handler.getHandlerType());

                              }

                        }

                  }

            }

      }



      /**

       * Gets the order.

       *

       * @param clazz the clazz

       * @return the order

       */

      private int getOrder(Class<?> clazz) {

            int order = Ordered.LOWEST_PRECEDENCE;

            Order orderAnnotation = clazz.getAnnotation(Order.class);

            if (orderAnnotation != null)

                  order = orderAnnotation.value();

            return order;

      }

                   

 

First, the priorotizeHandlers() method iterates through the list of detected Handlers. It checks the HANDLER_TYPE constant of each handler class.

  1. If the HANDLER_TYPE is unique (was not detected earlier) it is loaded to a java.util.Map ready to be registered.
  1. If HANDLER_TYPE is not unique, and a handler with the same type already exists in the handler map, then it reads the Order of both handler classes using calls to the getOrder() method. It then identifies which handler is of higher priority, and uses that instead.

                                

Please note that the unlike the previous method of handler registration using Spring key value pairs, the new method does not give priority to the handler classes based on a ‘first come --first served’ basis.