wiki:DependencyInjection

Dependendy Injection

Date 2012/03/26
Contact(s) Jesse Eichar
Last edited
Status draft
Assigned to release 2.9.x
Resources R&D Camptocamp
Code https://github.com/jesseeichar/core-geonetwork/tree/feature/injection
Ticket TBD

Overview

Use the Spring dependency injection framework for connecting the different components of Geonetwork and Jeeves and use the framework for managing object lifecycle, configuration etc...

Proposal Type

  • Type: Now Module
  • App: GeoNetwork
  • Module:

  • Email discussions:
  • IRC discussions:

Voting History

  • None as yet

UPDATE

I think the best hope of doing this proposal is to slowly migrate to a more and more spring wired application rather than doing at once.  I will start at the deep levels like the Datamanager etc... and work out.

Motivations

It has been noted that many companies currently maintain forked instances of Geonetwork with customizations for their system. The customizations are typically one of the following types:

  • Xsl customizations
  • Java customizations
  • Javascript customizations

The Javascript customizations are handled as best we can with the widget UI. The XSL customizations are an issue but Schemaplugins and User Formatters relieve this a little. However the java customizations typically require deep patches to the Geonetwork code which can be quite hard to migrate to new versions.

A solution is to modify Jeeves and Geonetwork to be wired together via a dependency injection framework instead of many references between many objects as is done now. The current solution is very tightly coupled and as a result very inflexible.

A converging issue is the desire to use spring security to manage security because it has much richer set of authentication options from what is in Geonetwork prior.

Since Spring has a powerful dependency injection framework and Geonetwork already has several dependencies on Spring and (specifically Spring context). It makes sense to use the Spring dependency injection framework.

Proposal

Advantages:

  • Using the Spring dependency injection framework it is simple to replace one implementation of an object with another. Many objects are already configurable like Resource providers and services but many are not. For example suppose you wanted to use a subclass of DataManager instead of the standard implementation. Currently that requires rebuilding Geonetwork. With spring that is a configuration detail.
  • The dependency injection also provides a simple plugin system. Currently Jeeves and sometimes other subsystems have plugins (like services and schedulers) and the code for parsing the xml, instantiating the classes injecting dependencies is all handled in the Geonetwork/Jeeves code. Spring takes care of this and provides a much more flexible and powerful plugin system than Jeeves.
  • In addition dependency injection makes it much easier to perform unit tests, for example a mock DataManager and Database Resource could be injected and a particular component or service could be tested with out requiring the full system to be running.
  • Spring already has the concept of "overrides" through inheritance and overriding beans thus making the need for a custom solution nearly obsolete
  • Spring provides some advanced features such as Aspect Oriented Programming. Some uses for this feature would be to inject an operation before and after any method in the system. For example one could add an operation each time a metadata is changed without needing to change the code of the core geonetwork at all. Another example would be to replace keep the implementation of DataManager but override a single method within DataManager.
  • Spring Security combined with dependency injection would allow access control on a per-method basis. For example one could designate specific methods that only Admin is permitted to execute and this can be done through configuration instead of code changes in the system.
  • With the addition of spring security into the system Geonetwork already has dependencies and even uses spring. Thus it makes sense to use more of its functionality.
  • Provides a migration path to other spring services like spring data for database access.
  • Reduces the amount of XML parsing done in Geonetwork (reducing the amount of code in Geonetwork)
  • Only one type of configuration (Geonetwork already has spring configuration for JZKit and spring security)
  • Spring developers (like those from Geoserver or a multitude of other projects) would have an easier time starting to work on Geonetwork.

Use cases:

  • Currently GetRecords only supports a few output formats, suppose you want to add more output formats. If the GetRecords, GetCapabilities, etc.. were injected it would be easy to intercept requests going to those services and handle extra formats or (in the case of GetCapabilties) modify the output of the service.
  • Currently if one want to have object available throughout the system (like DataManager or SearchManager) they have to be added to the Geonet object (or ServiceContext). However suppose one wants a set of services to be able to communicate together or share an object like a SearchManager then that requires substantial changes to the system. With Dependency injection it is again just a configuration detail (and of course implementation of the services and the object) and can be added to a release Geonetwork instance.
  • Making a statistics module would be much easier since it is very simple to be able to add wrap a method call in a method that will update the statistics. The wrapping method would have all the input and thus would have access to all the data required for calculating the search, edit, etc… statistics.
  • Suppose an application has extra restrictions on what users can access a certain metadata. With dependency injection the getMetadata method on DataManager or XmlSerializer have the appropriate methods wrapped by the dependency injection system to inspect each request and deny those that do not meet the new requirements.
  • Configuration files can have autocomplete if the spring eclipse extensions are installed.

Disadvantages:

  • The XML configuration is not as nice of a DSL since for large part it is a generic Spring configuration. (This issues is made less severe since Geonetwork already has some spring security and the configuration actually is pretty close to the current configuration)
  • The old configuration files will no longer work with the new system. There will be a process that runs on start up that will convert the old configuration to the new configuration but it cannot be guaranteed to work in all cases and some systems may need manual configuration.
  • Current Geonetwork developers will have to learn how the new system works since it is a major architectural change.

Dependency Injection Architecture

The first iteration will have an architecture that is very similar to the current architecture with some notable changes.

  • There are still two main layers. Jeeves and Geonetwork. The Jeeves module still has the primary concepts like Service, ResourceManager, ServiceManager, etc… and Geonetwork is still the application.
  • Geonetwork will no longer be responsible for creating classes like the DataManager, SchemaManager, SearchManager etc… Spring will create all of those classes and wire them together.
  • Geonetwork will instead be an "Initializer" that is responsible for preparing the non Java dependencies. For example setting up the database. Setting certain environment variable/properties that are required by several subsystems. It will be a Java Bean and therefore any object that requires one of those initialization services before being capable of running will depend on that Bean.
  • All major components will be Java Beans created and wired together by the dependency injection framework. Spring will ensure that the dependencies are created in the correct order.
  • Although the Geonet object is not necessary from an architectural point of view it will remain as is for developers who are used to the current framework.
  • Service will not be deprecated yes but they will be slowly migrated to a new IService interface that only has the exec method (the init method will not exist) because the initialization method code is unnecessary in the dependency injection system. The configuration file can be used to choose an arbitrary init and destroy method. A benefit of this is that services can have dispose methods if they desire.
  • The destroy or shutdown methods for many of the objects will be removed and only the beans that need cleanup will have then which the dependency injection framework will execute instead of the Jeeves code.
  • Similarly the startup and initialization code of most objects will be invoked by the dependency injection framework instead of the chain of calls required now.

Configuration

The structure of the configuration will be kept as similar as possible with the current configuration while still being spring configuration files. The one special case will be the service definitions. A special namespace will be created so that the service definitions are exactly the same as they are now. Here are a few comparisons:

New configuration of a service:

<j:service id="home">
     <output sheet="home.xsl"/>
</j:service>

Old configuration of a service:

<service name="home">
    <output sheet="home.xsl"/>
service>

As you can see the service configuration is almost identical. This is a special case because it is so common. A more typical example is of the general tag in the config.xml:

New configuration of a service:

<bean id="jeevesGeneralConfig"
     p:uploadDir="./data/tmp"
     p:debug="true"
     p:maxUploadSize="100"
     class="jeeves.config.GeneralConfig" />

Old configuration of a service:

<general>
     <profiles>user-profiles.xml</profiles>
     <!-- Size must be in megabyte (integer), 100MB by default -->
     <maxUploadSize>100</maxUploadSize>
     <uploadDir>./data/tmp</uploadDir>
     <debug>true</debug>
</general>

An alternative way to specify the config element could be:

<bean id="jeevesGeneralConfig" class="jeeves.config.GeneralConfig" >
     <property name="uploadDir" value="./data/tmp"/>
     <property name="debug" value="true"/>
     <property name="maxUploadSize" value="100"/>
</bean>

The second version is typically used if comments about the purpose of a property is desired or if the property is a map or list of values.

Backwards Compatibility Issues

The main backwards compatibility issue will be the configuration. Services, harvesters, scheduled tasks should continue to work as they do now. On startup the system will check the configuration files and attempt to migrate them to the new version. A copy of the original configuration files will be made so the administrator can revert the change or compare the old and new configurations As big or even bigger compatibility issue will be configuration overrides. They will be useable only for a small subset of cases. For example, to override SQL values and at the moment config-gui.xml is left alone so it will apply to that. The new way to override configuration will be to create a new spring configuration file and create a new bean of the same id as the bean you want to override. An additional (and more powerful) method is to register a BeanPostProcessor which will have access to all beans and can modify them via JavaBean setters.

Risks

  • Potential for a component to not be wired correctly (because the entire system is quite large) or a cyclic dependency could cause issues.
  • Configuration overrides would not work at all and there is not migration for that.

Participants

  • As above
Last modified 12 years ago Last modified on Oct 8, 2012, 1:01:54 AM
Note: See TracWiki for help on using the wiki.