While working on the new feature of the ability to add custom events to JIRA (available in the upcoming JIRA 3.6 release), I thought it would be of interest to discuss a point on adding new objects to the JIRA source through PicoContainer.
In Summary
A new object was introduced to the JIRA source that depended on a number of objects registered through PicoContainer and also one that was not registered. In order to adhere to the Dependency Injection concept, a factory object was introduced to provide the object with dependencies managed through PicoContainer and avoid static retrieval of these dependencies.


Background
Manager objects are the engine of JIRA. Each manager represents a particular concern and provides a service to achieve a particular goal. For example, the PermissionManager provides methods to check whether a user has the required permission to perform certain actions.
JIRA uses PicoContainer for dependency injection. In a JIRA nutshell:
* objects are registered with a central manager (ComponentManager)
* the ComponentManager manages the construction of all registered objects through PicoContainer
* each object requiring a reference to another registered object declares that dependency in its constructor
For convenience, the ComponentManager normally includes static methods to allow access to the registered objects – e.g.:

ProjectManager projectManager = ComponentManager.getInstance().getProjectManager();

This can be useful when an object’s constructor includes a non-registered object – i.e. an object not provided through PicoContainer.
However, this approach violates the Dependency Injection concept and can cause further problems in unit testing. For example, it is not possible to ‘mock out‘ a statically retrieved dependency for such references in writing unit tests.
Hence, to adhere to the concept of Dependency Injection, it is recommended to declare the dependency through the class constructor and let PicoContainer manage it.
Enter the Factory
The implementation of the new ‘custom event’ feature introduced the object TemplateContext. A TemplateContext encapsulates the information available to the Velocity context for rendering email templates. The TemplateContext object has the following dependencies:
* TemplateIssueFactory – allows creation of TemplateIssue objects
* FieldLayoutManager – used in rendering an issue event comment
* RendererManager – used in rendering an issue event comment
The object also included a dependency on an IssueEvent object that is not retrieved through the ComponentManager – so it is not possible to register the TemplateContext object through PicoContainer.
The simple option would be to statically retrieve these managers from the ComponentManager. However, in order to adhere to the Dependency Injection concept, the dependencies should be declared in the constructor and retrieved through PicoContainer:

public TemplateContext(IssueEvent issueEvent, TemplateIssueFactory templateIssueFactory, FieldLayoutManager fieldLayoutManager, RendererManager rendererManager)
{
this.issueEvent = issueEvent;
this.issue = issueEvent.getIssue();
this.templateIssueFactory = templateIssueFactory;
this.fieldLayoutManager = fieldLayoutManager;
this.rendererManager = rendererManager;
}

In order to retrieve these managers through PicoContainer, a TemplateContextFactory object was introduced:

public class DefaultTemplateContextFactory implements TemplateContextFactory
{
private final TemplateIssueFactory templateIssueFactory;
private final FieldLayoutManager fieldLayoutManager;
private final RendererManager rendererManager;
public DefaultTemplateContextFactory(TemplateIssueFactory templateIssueFactory, FieldLayoutManager fieldLayoutManager, RendererManager rendererManager)
{
this.templateIssueFactory = templateIssueFactory;
this.fieldLayoutManager = fieldLayoutManager;
this.rendererManager = rendererManager;
}
public TemplateContext getTemplateContext(IssueEvent issueEvent)
{
return new TemplateContext(issueEvent, templateIssueFactory, fieldLayoutManager, rendererManager);
}
}

This factory class declares the required dependencies and is registered with the ComponentManager, ensuring that these dependencies are managed through PicoContainer:

From ComponentManger.java:
internalContainer.registerComponentImplementation(TemplateContextFactory.class, DefaultTemplateContextFactory.class);

Each object requiring a TemplateContext object will delcare its dependency on the TemplateContextFactory in its constructor, and retrieve the TemplateContext object by calling the factory getTemplateContext(…) method.
With this approach, the TemplateContext is now correctly constructed with PicoContainer managed objects.
This approach can lead to an increased number of objects – but the simple implementation of the factory class should not increase the code complexity.

Fresh ideas, announcements, and inspiration for your team, delivered weekly.

Subscribe now