Last updated Mar 22, 2024

JIRA Developer Documentation: PicoContainer and JIRA

Picocontainer

JIRA uses Picocontainer as its central object factory and dependency injection framework. As such, Picocontainer is responsible for instantiating objects and resolving their constructor dependencies. This greatly simplifies code, in that any Picocontainer-instantiated object (for example a Webwork action) can obtain an instance of another (e.g. a Manager class) simply by requesting one in its constructor. PicoContainer will ensure that each object required by the constructor is passed in (a.k.a. dependency injection). For example, the ViewIssue action:

ViewIssue.java

1
2
public class ViewIssue extends AbstractViewIssue
{
    ....
    public ViewIssue(RepositoryManager repositoryManager, PermissionManager permissionManager, TrackbackManager trackbackManager,
                     ThumbnailManager thumbnailManager, SubTaskManager subTaskManager, IssueLinkManager issueLinkManager,
                     IssueLinkTypeManager issueLinkTypeManager, VoteManager voteManager, WatcherManager watcherManager,
                     PluginManager pluginManager)
   {
        super(issueLinkManager, subTaskManager);
        this.trackbackManager = trackbackManager;
        this.thumbnailManager = thumbnailManager;
        this.issueLinkTypeManager = issueLinkTypeManager;
        this.pluginManager = pluginManager;
        this.pagerManager = new PagerManager(ActionContext.getSession());
        this.repositoryManager = repositoryManager;
        this.permissionManager = permissionManager;
        this.voteManager = voteManager;
        this.watcherManager = watcherManager;
    }
    ....
}

Register new Picocontainer-managed classes

Picocontainer-managed classes need to be registered with Picocontainer. This happens automatically for Webwork actions, but other classes need to be registered manually. Jira does this in ContainerRegistrar's registerComponents() method:

ContainerRegistrar.java

1
2
public void registerComponents(final ComponentContainer register, final boolean startupOK)
{
    ...
        register.implementation(INTERNAL, EntityUtils.class);
        register.implementation(PROVIDED, AttachmentManager.class, DefaultAttachmentManager.class);
        register.implementation(PROVIDED, AttachmentService.class, DefaultAttachmentService.class);
        register.implementation(PROVIDED, ProjectService.class, DefaultProjectService.class);
        register.implementation(PROVIDED, FieldManager.class, DefaultFieldManager.class);
        register.implementation(PROVIDED, CustomFieldManager.class, DefaultCustomFieldManager.class);
        register.implementation(PROVIDED, CustomFieldService.class, DefaultCustomFieldService.class);
        register.implementation(PROVIDED, FieldScreenManager.class, DefaultFieldScreenManager.class);
        register.implementation(INTERNAL, DefaultFieldScreenStore.class);
        register.implementation(PROVIDED, MailThreadManager.class, MailThreadManagerImpl.class);
        register.implementation(PROVIDED, CvsRepositoryUtil.class, CvsRepositoryUtilImpl.class);
        register.implementation(INTERNAL, DefaultWebAttachmentManager.class);
        register.implementation(INTERNAL, I18nBean.class);// this is a candidate for removal (may not be used - SF 08/Oct/04)
        register.implementation(PROVIDED, I18nHelper.class, I18nBean.class);
        register.implementation(PROVIDED, I18nHelper.BeanFactory.class, I18nBean.CachingFactory.class);
        register.implementation(INTERNAL, JiraLocaleUtils.class);
        register.implementation(PROVIDED, LocaleManager.class, DefaultLocaleManager.class);
        register.implementation(INTERNAL, PingUrlFilterer.class);
    ...
}

Components can either be INTERNAL, meaning that they will be available only to JIRA itself, or PROVIDED, in which case they will also be available to plugins2 plugins.

Components are generally only registered in the ContainerRegistrar if they are required in JIRA internally. Plugin writers who wish to write their own components that can be injected into their plugin's classes should use the component plugin module.

Before Jira 9.0, if you wanted to register an alternative implementation of a pico-registered type, for example CustomFieldManager in the example above, you could do so by declaring a <component> module within a Plugins 1 plugin, with the child element <interface>com.atlassian.jira.issue.CustomFieldManager</interface>. From Jira 9.0 onwards, you can no longer override core Jira components in this way. Jira will simply log a warning about each attempted override, but will still allow the plugin to load.

Overriding components in JIRA with an extension pico-container

Deprecated:

Please note that this technique was removed in JIRA 8.0.

Sometimes it may be necessary for a plugin developer to override a component that JIRA ships with to provide some custom behaviour. You can do this by providing an extension pico container via a jira-application.properties property. In jira-application.properties, register an extension container provider:

1
2
jira.extension.container.provider = com.mycompany.jira.MyContainerProvider

In this class, you can register your own implementations of interfaces, which will be used in preference to the defaults in ComponentManager:

MyContainerProvider.java

1
2
package com.mycompany.jira;

import org.picocontainer.PicoContainer;
import org.picocontainer.defaults.DefaultPicoContainer;
import com.atlassian.jira.config.component.ProfilingComponentAdapterFactory;
import com.atlassian.jira.web.action.issue.BugAssociatorPrefs;
import com.atlassian.jira.security.PermissionManager;
import com.atlassian.jira.permission.PermissionSchemeManager;

import com.mycompany.jira.MyBugAssociatorPrefs;
import com.mycompany.jira.MyPermissionManager;
import com.mycompany.jira.MyPermissionSchemeManager;

public class MyContainerProvider implements ContainerProvider
{
    private DefaultPicoContainer container;

    public PicoContainer getContainer(PicoContainer parent)
    {
        if (container == null)
            buildContainer(parent);
        return container;
    }

    private void buildContainer(PicoContainer parent)
    {
        this.container = new DefaultPicoContainer(new ProfilingComponentAdapterFactory(), parent);
        container.registerComponentImplementation(BugAssociatorPrefs.class, MyBugAssociatorPrefs.class);
        container.registerComponentImplementation(PermissionManager.class, MyPermissionManager.class);
        container.registerComponentImplementation(PermissionSchemeManager.class, MyPermissionSchemeManager.class);
    }
}

Here we have registered our own implementations of three classes, after delegating to the default (so ours will take precedence). You can now keep MyContainerProvider and your modified com.mycompany.jira.\* classes in their own jar, which can be dropped into any JIRA instance to customise it to your needs.

Rate this page: