Uploaded image for project: 'Nuxeo Platform'
  1. Nuxeo Platform
  2. NXP-25647

Runtime Contributions Management

    XMLWordPrintable

    Details

    • Type: Epic
    • Status: Resolved
    • Priority: Minor
    • Resolution: Won't Do
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Runtime

      Description

      Descriptors management in Nuxeo

      Concepts and state of the art

      Nuxeo is made of modules articulated around one or several components, they define a set of functionalities eventually separated into various services, they are declared in XML files, often finishing by -service.xml, the recommended practice by now. Components in the code side are classes extending DefaultComponent.

      Components also define extension points (XP) that are possibilities for other modules to contribute to components behavior giving their own values and/or implementations, the extension points are described by java classes called descriptors, these classes are annotated with annotations from the XMap framework which is a homemade custom XML to object binding. To allow overriding of contributions, descriptor instances whith the same id can be merged.

      The contributions are made through extensions in XML files, often prefixed by -config.xml or -contrib.xml, also a way to go to ease browsing.

      The Nuxeo runtime parses extensions in XML files, instantiates corresponding descriptor objects and provide them to targeted components thanks to the method DefaultComponent#registerContribution.

      Problem

      There's no unified way to handle descriptor instances, nor there's a unified way to identify, store, merge and use them.

      Here's a list of some common practices.

      • Some components use them on the fly, while they are provided to them and don't save them, in some cases it does not cause harm, but that's definitely the worst way to manage descriptors as they are not 'saved' and cannot be merged.
      • Some components save them in a List, then use them on DefaultComponent#start, this last point is a good practice and should be generalized, by principle, as some components may have to use other components to handle their contributions, when start is called, all the components have been activated, and are then started depending on DefaultComponent#getApplicationStartedOrder. However storing them in a list is a bad practice as they are not merged or even worse they are merged with a custom way, not consistent with other merges.
      • Some other components use one or many dedicated specifications of SimpleContributionRegistry, this is the best way to go so far, still it's far from perfect as it implies to declare an inner class for each type of descriptors (from 1 to 3 generally) taking from 10 to 40 locs that bring no added values at all.

      Answer

      The solution to this situation is to use a unified DescriptorRegistry that handles all these mechanics and is provided by default in the DefaultComponent.
      DescriptorRegistry handles storing, merging and retrieving descriptors that implement Descriptor thus defines an id and a default merge implementation that can be overridden if needed.

      Moreover the registry keeps all descriptors as they were contributed and ordered. It merges them on the fly when requested.

      This implementation allows to use a single instance, stored in ComponentManagerImpl and passed to each DefaultComponent.
      Based on this, DefaultComponent will automatically forward Descriptor instances to its registry thus providing free-of-code descriptor management.

      Cost

      All Descriptor stuff is done and ready to use, but it has to be. It implies refactoring hundreds of descriptors to let them implement the interface and hundreds of components to rely on this registry instead of their current implementation.

      To prevent further wrong implementations SimpleContributionRegistry and its parent ContributionFragmentRegistry as been deprecated.

      Gain

      We will gain thousands of locs, consistency, and ease to write new components, so will our clients.

      Writing new components will be easier, having less code to write, especially boilerplate code, and the best practice is now embedded in the DefaultComponent making it also the natural and easy-to-find way to manipulate descriptors.

      Ideas

      This central, unique and unified way to store and use descriptors is a huge gain in itself.

      Leveraging it will also allow us to implement much easier modifications and evolutions of descriptors, and their use.

      For example we would be able to export and then import all the contributions saved in it almost for free. It could be used for debugging purpose or migrations. It could also allow to modify descriptors values being able to save and restore the state.

      Building a REST webservice on top of it could also allow to synchronize descriptors from an instance to another.

        Attachments

          Issue Links

            Activity

              People

              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: