As part of clearly defining the public, supported Java API for JIRA, several third-party libraries have been upgraded, previously deprecated classes have been removed and existing plugin points have changed behaviour and/or interface.
Changes in JIRA 5.0's core Java libraries, which differ from JIRA 4.4.x and earlier, are indicated in our Clirr report. This report is likely to change throughout the development of JIRA 5.0. If you are updating or developing plugins for JIRA 5.0, you may wish to check this page periodically.
Upgrading to Lucene 3.2 has caused a number of changes to the search interfaces of JIRA. The most notable is the replacement of the HitCollector class by the Collector class. There are also a number of smaller changes.
Search methods on the com.atlassian.jira.issue.search.SearchProvider
class now take a org.apache.lucene.search.Collector where previously they took a HitCollector
.
If you previously had a class that implemented HitCollector
you will need to modify it to implement the Collector interface. For most applications this is simple. See the example below.
1 2class NextPreviousHitCollector extends Collector { private final List<Integer> docIds = new ArrayList<Integer>(); private int docBase; NextPreviousHitCollector(final int currentIssueDocId) { this.currentIssueDocId = currentIssueDocId; } @Override public void collect(final int i) { int doc = docBase + i; docIds.add(doc); } @Override public void setScorer(Scorer scorer) throws IOException { // Do nothing } @Override public void setNextReader(IndexReader reader, int docBase) throws IOException { this.docBase = docBase; } @Override public boolean acceptsDocsOutOfOrder() { return true; } }
The notable points here are that more than one reader may be used by the search and that the document index returned in the collect method is related to the document base of the reader. You need to remember the document base that is set on the call to setNextReader
and add this to the value passed in the collect(int i)
method.
The acceptsDocsOutOfOrder()
method should generally return true as the actual ordering of the documents in the index is not relevant to JIRA in any way.
You should of course consult the Javadoc for the Collect interface.
The SearchProviderFactory.getSearcher()
method now returns org.apache.lucene.search.IndexSearcher instead of a Searcher. The methods of IndexSearcher
are largely compatible with those of the previous searcher interface, but some require an additional parameter specifying how many results you wish to have returned. You can specify Integer.MAX_VALUE
but performance may be better if the actual maximum number of results you want is specified.
Implementations of the NavigableField
interface must now return a FieldComparatorSource from the getSortComparatorSource()
method.
The FieldComparator
interface, which is returned by a FieldComparatorSource
, is quite different from the previous SortComparator
, but as most abstract implementations of NavigableField
already implement this method, it is unlikely to affect most plugin developers.
With the introduction of Embedded Crowd in JIRA in 4.3, many methods which referred to the old org.opensymphony.user.User
and org.opensymphony.user.Group
classes were deprecated and a thin emulation layer implemented to allow a simplified transition for plugin developers to the new Crowd User and Group classes.
In JIRA 5.0, this emulation layer has been removed. Consequently, org.opensymphony.user.User
and org.opensymphony.user.Group
and a small number of supporting classes have been removed. Furthermore, all deprecated methods have been either changed to use the new Crowd User and Group classes or removed (where they were simply duplicated by new Crowd versions).
Most plugins should just need recompiling.
The OpenSymphony Group class had methods for getting the members of the group and for adding and removing members. The OpenSymphony User class had similar methods for finding the groups a user was a member of. Use the new GroupManager
component for getting members of a group or adding user to a group. For more low-level manipulation of groups and memberships use the CrowdService
component.
Exceptions thrown by some methods may have changed. Hence, plugin developers will need to adjust their code accordingly.
OpenSymphony classes generally threw EntityNotFoundException
when a user or group was not found. The Crowd implementation will return null if a user or group is not found. So in places where you caught EntityNotFoundException, you will now need to test the return value for null.
JIRA 4.0 introduced gadgets to replace the old portlets that were used in earlier versions of JIRA. From JIRA 4.0, we still shipped all the code for portlets to:
In JIRA 5.0, we've finally removed all portlet-specific code from JIRA plugin points and APIs. Some plugins implement upgrade tasks to convert portlets to gadgets. These upgrade tasks may now fail if they depend on portlet specific APIs. These upgrade tasks should either be removed (since upgrading to JIRA 4.0+ should have removed portlets already anyway), or rewritten so that they do not rely on the portlet API.
JIRA 5.0 changes the way in which tabs are rendered on the view issue page. Previously the <issue-tabpanel> module was always rendered in the same request as the View Issue screen. In JIRA 5.0, however, the issue tab panel HTML may returned by an AJAX call and inserted into the view issue page without triggering a full page reload. This has two important implications:
Since IssueTabPanel.getActions(Issue, User) is now called in a separate request from the one that renders the view issue page, calling WebResourceManager.requireResource(java.lang.String) does not guarantee that the resource will be included on the view issue page. To make sure that resources are included, use the jira.view.issue web resource context in your <web-resource> definition as in the following example.
1 2<web-resource key="myPluginViewIssue" name="MyPlugin's Javascript for the View Issue page"> <context>jira.view.issue</context> <dependency>jira.webresources:viewissue</dependency> <resource type="download" name="myPluginViewIssue.js" location="script/myPluginViewIssue.js"/> </web-resource>
If your plugin contains Javascript that needs to execute when the tab is loaded, it should register a callback with JIRA.ViewIssueTabs.onTabReady()
instead of using AJS.$(document).ready()
. For example, if a JIRA 4.4 plugin contains the following Javascript file:
1 2AJS.$(document).ready(function () { AJS.$('.tab-content').find('.project-activity:gt(0)').addClass('hidden'); })
This file should be changed to the following in order to work in JIRA 5.0.
1 2JIRA.ViewIssueTabs.onTabReady(function() { AJS.$('.tab-content').find('.project-activity:gt(0)').addClass('hidden'); })
The class hierarchy of the various CustomFieldType classes has been changed. The following diagrams show the old hierarchy and new hierarchy. Removed relationships are marked in red, and new relationships are marked in green.
Old CustomFieldType Hierarchy
New CustomFieldType Hierarchy
What will break?
Please check your instanceof
's comparisons to ensure they are still behave as expected.
CustomFieldType has now been parameterized based on its Transport Object. This should hopefully simplify implementations and make it much easier to figure out whats going on. The generics are defined as follows:
1 2public interface CustomFieldType <T, S>
T represents the Transport Object of a Custom Field Type. This is can be thought of as the in-memory data representation, or logical representation. For example this could be a User or a Project, or maybe a Collection of Users. T can contain a single data type or an aggregate data type such as a Collection. JIRA needs to know what is inside this aggregate. S represents the single form of the T. In the case where it is a single data type, T and S will be the same.
Currently we support the following aggregate Transport Objects: Collection<S>, Map<String, S> or Map<String, Collection<S>>. N.B. Support for CustomFieldParams as the Transport Object has been deprecated since 5.0.
We have two abstract class which we recommend you use to help simplify some of this.
1 2public abstract class AbstractSingleFieldType<T> extends AbstractCustomFieldType<T, T>
1 2public abstract class AbstractMultiCFType<S> extends AbstractCustomFieldType<Collection<S>, S>
What will break?
Because we have typed all of our own CustomFieldType implementations, this means you may get method signature compilation problems if you are extending a concrete class. E.g. a NumberCFType now returns Doubles where it used to return Object.
Because of the large number of plugins extending the StringCFType and the TextCFType we have not genericised these classes, to assist with backwards compatability. However, we strongly suggest migrating to the new GenericTextCFType class.
StringCFType - use GenericTextCFType instead
TextCFType - use GenericTextCFType instead
AbstractMultiSettableCFType - implement the methods yourself.
Using CustomFieldParams as your Custom Field Transport Type (T or S) - use one of the other aggregates supported (see above)
Transform methods on the CustomFieldParams - CustomFieldParams should only be containing Strings, so therefore these should not be used.
The CascadingCFType has had a complete overhall. It was incorrectly using the CustomFieldParams map so now has been updated to use a Map for representation of the selected Options. Database storage remains the same. If you were extending or using this class there is a high chance of breakage.
AbstractMultiCFType received a bit of an overhall in 5.0. It used to assume that everything was stored as a String, but this is no longer the case. This class has also been further parameterized. This has resulted in:
convertToStringsIfRequired
no longer does the "if required" bit, it will now always convert to strings.SortableCustomField
by default. Include this yourself if requiredRate this page: