OpenLMIS Extension points and modules

Hi,

···

We are working on the proof of concept. Our idea is based on creating a new directory where extension module’s jar will be stored. The directory path will be defined in base module. It will be possible to add this jars manually or through gradle task. Path to base module will be indicated in settings.gradle in extension module.

  Best regards,

  Klaudia Pałkowska

    W dniu 12.07.2016 o 19:50, Josh Zamor pisze:

Hi Klaudia,

      This is looking good, I'll dive into it later today.  Something that doesn't appear to be covered, that we've needed to concretely answer is how extension points will be packaged, delivered, and used by an implementer.  The architecture document calls out that we'd planned to package extension modules using Maven and then pull in the new Spring components at a Service's build time.  It would be very useful to have an example of that working so that we're sure that we get a solid feel for how well this approach will work for both developers and implementers.



      Thanks!



      Best,

      Josh

To write what I tried to say on this morning’s call:

I have some concerns about this solution and/or about the problem statement that led to it.

The world I expect us to be looking at is:

  • “Core teams” build services/features that have extension points
  • Project teams build custom extensions, which can modify/extend the behaviors of core services/features
  • Usually the driver for this will be a specific country implementation
  • Other country projects (and also the funders of future projects) will want to leverage pieces of work that have been done by all the projects that came before them.
  • I.e. they will take “core” services + mix and match extensions written in other countries + a few more extensions of their own
    In this model, I don’t think that the model of “in the extension we use the @Primary annotation to override the default behavior” solves the right problem.

Instead I would expect the decision about which extensions to activate (e.g. which algorithms to use, which menu shortcuts to display in which order, etc) are not decided by the author of the extension, but rather by the implementation that uses/configures them.

So “which extensions are activated +/- in which order” should not be decided in the core or extension code, but rather by “implementation configuration”, i.e. purely by configuration files. (I forget the exact architecture, but Jake suggests that this config could live in the implementation’s fork of the reference application.)

So, the core extension framework needs to support the idea that external configuration (this could be spring xml, though I’d hope it can be something much simpler for an implementer to configure) decides how extensions are wired to extension points.

-Darius

···

On Wed, Jul 13, 2016 at 6:54 AM, Klaudia Pałkowska kpalkowska@soldevelo.com wrote:

Hi,
We are working on the proof of concept. Our idea is based on creating a new directory where extension module’s jar will be stored. The directory path will be defined in base module. It will be possible to add this jars manually or through gradle task. Path to base module will be indicated in settings.gradle in extension module.

  Best regards,

  Klaudia Pałkowska


    W dniu 12.07.2016 o 19:50, Josh Zamor pisze:

Hi Klaudia,

      This is looking good, I'll dive into it later today.  Something that doesn't appear to be covered, that we've needed to concretely answer is how extension points will be packaged, delivered, and used by an implementer.  The architecture document calls out that we'd planned to package extension modules using Maven and then pull in the new Spring components at a Service's build time.  It would be very useful to have an example of that working so that we're sure that we get a solid feel for how well this approach will work for both developers and implementers.



      Thanks!



      Best,

      Josh

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to openlmis-dev@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/openlmis-dev/57864809.1090309%40soldevelo.com.

For more options, visit https://groups.google.com/d/optout.

Darius JazayeriPrincipal Architect - Global Health
Email
djazayeri@thoughtworks.com

Telephone
+1 617 383 9369

ThoughtWorks

Hi everyone,
Josh, thank you for your opinion.
Darius, you are right - it is important to provide possibility to mix and match extensions.
We think that the decision about which extensions should be activated can be made by implementers by configuration file.
Small example of how it could look like:

AMCOrderQuantity org.openlmis.example. OrderQuantity org.openlmis.example-extension.AMCOrderQuantity

Then, we see two soulutions how to activate the right extensions defined in this configuration file.

First of them is to write our own Manager (ex. ExtensionsManager) that will have getImplementation method that will return implementation based on configuration file.
This manager will be used to retrieve beans instead of @Autowired annotation.
So if the given extension point has an extension defined in this file, our Manager will return implementation defined in extension module. If not, he will return the default implemetation.
To know which implementation is default, we can create our own @DefaultImplementation annotation.

The second solution is to write class implementing FactoryBean to create custom bean factory.
Overriding getObject() method would allow us to choose which bean will be returned if there is more than one implementation of given extension point.
It will work similar to the first solution, by reading configuration file bean factory will decide which object should be returned. Also, @DefaultImplementation annotation would be useful here.

We will be glad to hear your opinions, and if you have any questions we will be happy to answer them.
When one of the solutions will be chosen, we can start to work on example implementation.

Best regards,
Weronika

···

On Wednesday, 13 July 2016 23:31:27 UTC+2, djazayer wrote:

To write what I tried to say on this morning’s call:

I have some concerns about this solution and/or about the problem statement that led to it.

The world I expect us to be looking at is:

  • “Core teams” build services/features that have extension points
  • Project teams build custom extensions, which can modify/extend the behaviors of core services/features
  • Usually the driver for this will be a specific country implementation
  • Other country projects (and also the funders of future projects) will want to leverage pieces of work that have been done by all the projects that came before them.
  • I.e. they will take “core” services + mix and match extensions written in other countries + a few more extensions of their own
    In this model, I don’t think that the model of “in the extension we use the @Primary annotation to override the default behavior” solves the right problem.

Instead I would expect the decision about which extensions to activate (e.g. which algorithms to use, which menu shortcuts to display in which order, etc) are not decided by the author of the extension, but rather by the implementation that uses/configures them.

So “which extensions are activated +/- in which order” should not be decided in the core or extension code, but rather by “implementation configuration”, i.e. purely by configuration files. (I forget the exact architecture, but Jake suggests that this config could live in the implementation’s fork of the reference application.)

So, the core extension framework needs to support the idea that external configuration (this could be spring xml, though I’d hope it can be something much simpler for an implementer to configure) decides how extensions are wired to extension points.

-Darius

On Wed, Jul 13, 2016 at 6:54 AM, Klaudia Pałkowska kpalk...@soldevelo.com wrote:

Hi,
We are working on the proof of concept. Our idea is based on creating a new directory where extension module’s jar will be stored. The directory path will be defined in base module. It will be possible to add this jars manually or through gradle task. Path to base module will be indicated in settings.gradle in extension module.

  Best regards,

  Klaudia Pałkowska


    W dniu 12.07.2016 o 19:50, Josh Zamor pisze:

Hi Klaudia,

      This is looking good, I'll dive into it later today.  Something that doesn't appear to be covered, that we've needed to concretely answer is how extension points will be packaged, delivered, and used by an implementer.  The architecture document calls out that we'd planned to package extension modules using Maven and then pull in the new Spring components at a Service's build time.  It would be very useful to have an example of that working so that we're sure that we get a solid feel for how well this approach will work for both developers and implementers.



      Thanks!



      Best,

      Josh

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev...@googlegroups.com.

To post to this group, send email to openlm...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/openlmis-dev/57864809.1090309%40soldevelo.com.

For more options, visit https://groups.google.com/d/optout.

Darius JazayeriPrincipal Architect - Global Health
Email
djaz...@thoughtworks.com

Telephone
+1 617 383 9369

ThoughtWorks

(Sorry for the slow reply)

Yes, you have understood what I was getting at, and its implications.

The configuration file should either be spring application context xml (if this makes the implementation easier/cleaner), or else it should be much simpler.

It may be sufficient to have just key=value, like:

example.OrderQuantity = org.openlmis.example-extension.AMCOrderQuantity

Or else it could be YAML or similar if we want more power. But let’s not ask people to write verbose xml if it’s not the actual xml that will drive our system.

Particular extension examples I can think of quickly are (1) indicating one specific spring bean to fulfill an interface (like this order quantity example), or (2) a list of shortcuts to show in a menu.

I think the spring factory bean option is clever, but I don’t like it because I think we’ll run into the scenario where you want multiple beans instantiated, but only one of them chosen for a particular thing. (E.g. 3 “core” calculation algorithms all have beans instantiated +/- any provided by extensions; one is chosen for the Malaria program, and another for the vaccine program.) Also, I don’t think it will handle the “list of shortcuts” example very well. So, I recommend the first, having an ExtensionManager bean. (This also make it very explicit what is happening, i.e. a service’s code shows you that it’s fetching an extension, rather than relying on invisible spring magic.)

For the default implementation, my first thought is to specify this in a base configuration file, that is either forked or overridden by the implementations. This will make it explicit and clear for the ultimate implementers/configurers. (However if that makes writing unit tests hard, @DefaultImplementation could be a better way to go.)

-Darius

···

On Thu, Jul 14, 2016 at 7:03 AM, Weronika Ciecierska wciecierska@soldevelo.com wrote:

Hi everyone,
Josh, thank you for your opinion.
Darius, you are right - it is important to provide possibility to mix and match extensions.
We think that the decision about which extensions should be activated can be made by implementators by configuration file.
Small example of how it could look like:

AMCOrderQuantity org.openlmis.example.OrderQuantity org.openlmis.example-extension.AMCOrderQuantity

Then, we see two soulutions how to activate the right extensions defined in this configuration file.

First of them is to write our own Manager (ex. ExtensionsManager) that will have getImplementation method that will return implementation based on configuration file.
This manager will be used to retrieve beans instead of @Autowired annotation.
So if the given extension point has an extension defined in this file, our Manager will return implementation defined in extension module. If not, he will return the default implemetation.
To know which implementation is default, we can create our own @DefaultImplementation annotation.

The second solution is to write class implementing FactoryBean to create custom bean factory.
Overriding getObject() method would allow us to choose which bean will be returned if there is more than one implementation of given extension point.
It will work similar to the first solution, by reading configuration file bean factory will decide which object should be returned. Also, @DefaultImplementation annotation would be useful here.

We will be glad to hear your opinions, and if you have any questions we will be happy to answer them.
When one of the solutions will be chosen, we can start to work on example implementation.

Best regards,
Weronika

On Wednesday, 13 July 2016 23:31:27 UTC+2, djazayer wrote:

To write what I tried to say on this morning’s call:

I have some concerns about this solution and/or about the problem statement that led to it.

The world I expect us to be looking at is:

  • “Core teams” build services/features that have extension points
  • Project teams build custom extensions, which can modify/extend the behaviors of core services/features
  • Usually the driver for this will be a specific country implementation
  • Other country projects (and also the funders of future projects) will want to leverage pieces of work that have been done by all the projects that came before them.
  • I.e. they will take “core” services + mix and match extensions written in other countries + a few more extensions of their own
    In this model, I don’t think that the model of “in the extension we use the @Primary annotation to override the default behavior” solves the right problem.

Instead I would expect the decision about which extensions to activate (e.g. which algorithms to use, which menu shortcuts to display in which order, etc) are not decided by the author of the extension, but rather by the implementation that uses/configures them.

So “which extensions are activated +/- in which order” should not be decided in the core or extension code, but rather by “implementation configuration”, i.e. purely by configuration files. (I forget the exact architecture, but Jake suggests that this config could live in the implementation’s fork of the reference application.)

So, the core extension framework needs to support the idea that external configuration (this could be spring xml, though I’d hope it can be something much simpler for an implementer to configure) decides how extensions are wired to extension points.

-Darius

On Wed, Jul 13, 2016 at 6:54 AM, Klaudia Pałkowska kpalk...@soldevelo.com wrote:

Hi,
We are working on the proof of concept. Our idea is based on creating a new directory where extension module’s jar will be stored. The directory path will be defined in base module. It will be possible to add this jars manually or through gradle task. Path to base module will be indicated in settings.gradle in extension module.

  Best regards,

  Klaudia Pałkowska


    W dniu 12.07.2016 o 19:50, Josh Zamor pisze:

Hi Klaudia,

      This is looking good, I'll dive into it later today.  Something that doesn't appear to be covered, that we've needed to concretely answer is how extension points will be packaged, delivered, and used by an implementer.  The architecture document calls out that we'd planned to package extension modules using Maven and then pull in the new Spring components at a Service's build time.  It would be very useful to have an example of that working so that we're sure that we get a solid feel for how well this approach will work for both developers and implementers.



      Thanks!



      Best,

      Josh

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev...@googlegroups.com.

To post to this group, send email to openlm...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/openlmis-dev/57864809.1090309%40soldevelo.com.

For more options, visit https://groups.google.com/d/optout.

Darius JazayeriPrincipal Architect - Global Health
Email
djaz...@thoughtworks.com

Telephone
+1 617 383 9369

ThoughtWorks

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to openlmis-dev@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/openlmis-dev/0de4bda4-8f42-409e-be24-a6e675258a76%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Darius JazayeriPrincipal Architect - Global Health
Email
djazayeri@thoughtworks.com

Telephone
+1 617 383 9369

ThoughtWorks