Mule 3.7 is approaching, and among other things we decided to put a lot of focus on the experience of the guy coding custom components (Devkit based or not). For this, the first 3.7 milestone is incorporating big changes in terms of the how Mule Lifecycle and Dependency Injection are applied.
As I said, these changes are BIG, so for clarity reasons we’ll cover them in a series of post starting with this one. Hopefully by the time 3.7 is out, you’ll already be familiar with all the new goodies at your disposal.
The discussion about to start is going to be highly technical, it’s mainly intended for developers coding their own custom Mule components. If you’re the kind of person who codes their own components or finds it tasteful to leverage the low level Mule APIs and internals, then you should definitively read this series of posts. On the other hand, if you’re the kind of user which just simply uses Mule out-of-the-box and the Anypoint Studio palette gives you all you need, then you probably won’t be as excited about this long read, but it’s recommended anyway; it’s always good to know your tools.
A little bit of Mule Anatomy
The MuleContext contains the concept of an object registry. The mule registry is basically a set of key-value pairs used to gain access to relevant mule components, such as connectors, factories, object stores, managers (such as the QueueManager), etc. This is usually known as an Object Container.
- Registries in Mule have a number of attributes:
They are composable via a RegistryBroker which allows N registries to be added and used.
- The registry implementation is decoupled via a Registry interface. This is so to achieve two goals:
- To allow the core mule to not depend on a third party container such as Spring or Guice (although the
Spring based registry is the only implementation currently used)
- Allowing users running in embedded mode to plug their own object container.
As a result, when a MuleContext is created, it creates a registry broker with two registries;
i) An instance of TransientRegistry, which is a very basic implementation of the Registry contract
ii) On top of that, the spring-config module also adds a SpringRegistry, which is another implementation of Registry which relies on Spring.
The TransientRegistry contains:
- All the objects declared in a registry-bootstrap.properties file
- Any objects created and registry manually via muleContext.getRegistry().registerXX(), which usually happens in CoreExtension and Agent instances
The Spring Registry contains:
- Every component that was created through an XML config (connectors, configs, flows, message processors, etc).
Spring is then used to host most of the registry objects, including every component which is created from reading an application’s XML configuration (since Spring is used to parse the XML). The TransientRegistry is given priority over every other registry. When an object is requested from the main registry, it searches all registries until a match is found. Because the TransientRegistry goes first, its objects will always have priority.
Interoperability and dependency injection
Because the objects live in two separate registries, then some issues arise:
- Dependency Injection between registries is out of the question. If an object in registry A depends on an object X from registry B, there’s no way to perform that injection (registries start in order)
- Objects in the TransientRegistry cannot access the ones in other registries. For example, the QueueManager lives in the TransientRegistry. That means that when it’s initialised it cannot access the main mule configuration, because such configuration is set through XML and thus lives in the Spring registry, which is started later.
- Overrides are inconsistent. For example, the Cluster extension replaces the default QueueManager and ObjectStore instances by cluster aware versions. Because the TransientRegistry has priority, those are only replaced there. That works fine when doing a manual lookup, but if a component uses Spring dependency injection to get a QueueManager, it will get the local version instead.
The bottom line is that there’s a functional dependency between the TransientRegistry and the SpringRegistry, due to the fact that:
- The Spring registry contains the SimpleRegistryBootstrap
- The Spring registry contains the MuleContext
- Because the Spring registry starts flows, connectors, batch jobs and other components that depend on objects started through the registry bootstrap, we can’t start the spring registry before the TransientRegistry.
These issues prevents us from:
- Have a solid IoC mechanism
- Have a consistent lifecycle across registries
Bringing a Mule to life
Finally but definitely not least, the probably most serious issue with the current registry implementation is how it manages lifecycle. Although this is the most severe problem, I saved it for last because it’s greatly influenced by all the other problems.
The registry has an additional responsibility aside from the ones already described, which Ross Mason described as follows when he first came out with the concept: “The registry is a magic bucket which ensures that all objects are in the same lifecycle state”.
The lifecycle is comprised by 4 phases:
This is conceptually fine, but the implementation has a lot of problems. The lifecycle is not implemented as something that leverages the registry to make sure that all the important components are transitioned harmoniously. Instead, each registry is in charge of portions of that lifecycle, while some others are handled separately. This leads to inconsistent behaviour:
- The transient registry performs the initialise() operation whenever an object is registered. This means that if the object depends on some other which hasn’t still been registered, it won’t find it (I’m not talking about dependency injection here, this doesn’t work even with a 90’s like lookup)
- The SpringRegistry on the other hand, automatically executes the initialise phase when the bean is instantiated, which means that the initialising object might depend on other objects which are not yet instantiated. This only happens with objects that mule registers on its own, if the user registers a spring bean, lifecycle is not applied to it.
- The SpringRegistry also shows a similar behaviour towards the dispose phase.
- Because the functionality is part of the registry instead of leveraging it, each new registry can potentially have a different behaviour.
Make the Change!
So if you made it this far through all the boring technical definitions and silly mule images, it’s time to actually see what will be different in Mule 3.7!
- Registry unification: In runtime there will be one and only one registry by default: The Spring one. This will make it possible to have a consistent IoC mechanism. You’ll still be able to add your custom registries but that mechanism is deprecated as of this version. Manually added registries will not participate in dependency injection.
- Support for JSR-330 annotations: You custom components will now be able request dependency injection by the use of the @Inject annotation!
- Registry and Lifecycle are now separated: Although the lifecycle mechanism heavily relies on the registry, these are now two separate concepts, handled by separate components.
All the changes above, made it easy to fix all of the lifecycle bugs we had.
Ok, this is way too much information for one single post. Please read the next post on this series where we talk about the internals of how this was implemented and migrations considerations when the time comes to adopt Mule 3.7.