Howdy! This is the last post of the Mule 3.7 series Be sure to check out the other posts in the series:
- A sneak peek into Mule 3.7’s deepest internals »
- One to Contain them All: Unifying the Mule Registry in 3.7 ».
IMPORTANT: If you haven’t read the previous posts in this series, do so now. This will be very difficult to follow otherwise. This post carries the same warning as the first one: 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.
Quick recap of Mule 3.7 posts
So what we learned in the previous posts:
- Mule has a registry that acts as an object container which is decoupled from the actual technology used to fuel it (in our case, the Spring framework)
- That registry is composable and objects are split across two different registries, which leads to inconsistencies and prevents a solid dependency injection mechanism
- Objects in that registry have a lifecycle, which Mule doesn’t apply in a consistent manner
We also discussed how, in Mule 3.7, we’re tackling these issues by unifying all of Mule’s managed objects into one single and unique registry (Spring). The only missing piece after doing that is to rely no longer on the object container to fire the lifecycle phases anymore, which is what this post is about.
The Magic bucket
Before continuing, we need to ask ourselves what’s the relationship between the Mule Registry and the lifecycle. Put in the words of Mule’s founder and creator Ross Mason: “The registry is a ‘magic bucket’ which ensures that all the objects it contains are in the same lifecycle state”.
So the registry is not only for registering and locating sensible Mule components, but it’s also to ensure that they are all in a consistent lifecycle state, which ultimately is a requisite for them to be able to interact.
If we consider the registry as this “magic bucket” which makes all components transition together, then yes, registries are still responsible for firing those phases on the objects they own. But how to apply those phased once fired is a separate concern. Otherwise, there’s no way to guarantee that the lifecycle will be applied in a uniform and consistent way, regardless of the Registry implementation (SimpleRegistry will behave differently from a SpringRegistry which in turn will be different from a hypothetical GuiceRegistry).
In this way, the Registry interface continues to extend the Lifecycle one, but lifecycle will only be applied once:
- All objects are instantiated
- All objects have been fully injected
- All post-processors have been executed
- A LifecycleManager instance is available to make this transitions. No manual invocation of lifecycle methods at a registry level
Notice that this is very different from what happens on Mule 3.6.x and before, in which:
- The TransientRegistry eagerly calls the initialise phase on which ever object is registered on it
- Spring invokes the initialise phase on every object as soon as it instantiates it, which in some cases can lead to a wrong initialisation order
Initialisation order
Per the discussion in the point above, the correct initialisation order was not being respected by either the TransientRegistry nor the Spring one. Now that initialisation only fires once all objects are injected, the order being respected becomes more relevant.
The initialisation order:
- ObjectStoreManager
- ExpressionEvaluator
- ExpressionEnricher
- ExpressionLanguageExtension
- ExpressionLanguage
- Config
- Connector
- Agent
- Model
- FlowConstruct
- Initialisable
Notice that a new interface has been added: Config. That is a new marker interface that is to be implemented by configs of new connector-ish modules such as the new HTTP connector introduced in 3.6. For now, it’s just an empty interface, but we reserve the right to grow it.
GOTCHA: You might be wondering what happens if a given object implements more than one of those interfaces. There’s logic present to ensure that the same object is not initialised twice.
The Tree of Life (a.k.a. the dependency tree)
The initialisation order described above is good, but not enough in all cases. What happens if a user creates and registers a custom ObjectStoreManager implementation which depends on another registered component, which implements the ExpressionLanguage interface? If this custom ObjectStoreManager tries to access that ExpressionLanguage during the initialisation phase, then it will probably end up in an exception since the latter has not yet been initialised.
All these changes enable us to not only start in a type based order, but also apply a second level of ordering based on dependencies. Before we go down to the practical example, let’s see another theoretical one… Suppose the following tree of dependencies:
Once Mule decides it’s time for object ‘h’ to be initialised, then the initialisation order will be: a, b, c, d, e, f, g, h
Now for the practical example, let’s see how the custom ObjectStoreManager described above would look like:
In this example, the javax.inject.Inject annotation is used to hint Mule into the dependency between these two objects. This will of course work only as long as the dependency is expressed through a Spring bean definition or by the use of JSR-330 annotations such as @Inject. Otherwise, Mule won’t have the necessary information to build a “tree of life”.
GOTCHA 2: The SimpleRegistry implementation that we talked about in the first post does not support trees of life. But that’s fine, because as it was stated, that registry implementation is for testing only.
Injecting external objects
At this point, we have already discussed fixes and improvements that fulfill all issues and wishes listed on the first post of this series. But let’s close this with a new goodie, shall we?
So far we’ve discussed how we can now use dependency injection to wire objects in the registry together. But wouldn’t it be cool is we could also inject dependencies into objects that are not necessarily registered? For example, suppose you are building a custom component which uses the command design pattern. And suppose that you have a command which requires the MuleContext, the ObjectStoreManager, and the QueueManager while also carrying some custom state which prevents you from making that instance reusable (as it’s often the case with the command pattern). Such a command object could look like this:
Because you can potentially need one instance of each of those commands per MuleEvent your component receives, it makes no sense to register each instance just for the sake of dependency injection. However, it would be really neat to be able to fulfil those @Inject statements. Well, it turns out you now can:
Starting with 3.7, there will be a new API in Mule called Injector which, not surprisingly, is capable of injecting dependencies into any JSR-330 annotated object. The fun thing is that injected instances do not go into the registry just because of it.
End of the Mule 3.7 series
Well, that’s it, that’s the end of the Mule 3.7 series. I hope you’re excited about these improvements! As always, please do send your feedback. It really helps us know how things look, and it’s your chance to influence the product greatly!
Thank you for reading!