Jira 8.0.0- EAP02 - SearchResults.getIssues() failure

eap

#1

Hi,

just have been testing Jira 8, EAP.

Found the following problem:
import com.atlassian.jira.issue.search.SearchResults;

SearchService searchService = ComponentAccessor.getComponent(SearchService.class);
ApplicationUser currUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();

	SearchResults searchResult = null;

	try {
		// get results for the jql query

		searchResult = searchService.search(currUser, queryToExecute, PagerFilter.getUnlimitedFilter());
		
		// output search Result
		issues = searchResult.getIssues();
               ...
         }

When running this (provide a valid JQL Query in queryToExecute) on Jira 8.0.0-EAP02, i get the following error:
NoSuchMethodError: com.atlassian.jira.issue.search.SearchResults.getIssues()Ljava/util/List;, stacktrace=java.lang.NoSuchMethodError: com.atlassian.jira.issue.search.SearchResults.getIss
ues()Ljava/util/List;

According to Jira documentation 7.12.1, SearchResult is NOT marked as deprecated. In addition SearchResult is marked as @PublicApi.

Can anybody help here?

If case this is a bug in Jira 8.0.0-EAP02, does anybody know, where to post the issue?


#2

Have you tried compiling against Jira 8? I believe I have the same problem locally, the method signatures for the class have changed in the EAP.


#3

Thanks for asking!

Jira 8.0 is a platform release, meaning there will be breaking API changes. Please read Preparing for Jira 8.0 for more detail.

As @reece commented, please compile your plugin code against the EAP02. In this particular case the getIssues method was renamed to getResults. You will find more information on this when you search for “com.atlassian.jira.issue.search.SearchResults” in Preparing for Jira 8.0/Lucene Upgrade.

Sorry for late response.


#4

Hi,

this is not the solution.

  1. Taken from: https://docs.atlassian.com/software/jira/docs/api/7.12.2/com/atlassian/jira/issue/search/SearchResults.html
    Class is marked with @PublicAPI

  2. Method getIssues() is NOT marked as depracted

  3. Method getResults() DOES NOT EXIST (in Jira prior 8.x).

This would violate Atlassian Policy regarding usage of @PublicApi (at least if you consider the intention of the @PublicApi annotation).

I understand, that you want to get rid of the method getIssues() in the SerarchResults interface, but I do not understand, why you are not intoducing getResults in Jira 7.13 or earlier? This would solve the problem.

Any possible solution you can provide? We need a method available and usable in Jira 7.x as well as 8.x.

Thank you,

iConcept Support


#5

Hi,

This would violate Atlassian Policy regarding usage of @PublicApi (at least if you consider the intention of the @PublicApi annotation).

Could you please send a link to the policy you are referring to? I found this one:
https://developer.atlassian.com/server/jira/platform/java-api-policy-for-jira-4227213/

and here are the important excepts:

  • Major Release
    A major release is a ‘.0’ release.
    For example: 5.0, 6.0, 7.0, …

Compatibility Policy

The JIRA API (all classes and other components in the jira-api module) will remain forward-compatible throughout all the minor releases within a major release family.

For example, a plugin that is built against JIRA 5.0 will continue to work with JIRA 5.1, v5.2, and so on, without being recompiled.

Major Releases

A major release may change the existing API.

Normally, we will attempt to mark the method or class as deprecated in a previous minor release, but this is not guaranteed.

In general, it may not be possible to create a single plugin artifact that is compatible with different major release families.

For example, it may not be possible to create a single JAR for your plugin that is compatible with both JIRA 5.0 and JIRA 4.4.

I understand that it can be painful to support two major releases in a plugin (we know this ourselves, as we support multiple bundled plugins), but with such a big change it is impossible to make it binary compatible across two major releases. In particular, we cannot provide two versions of Apache Lucene, Guava or jQuery at the same time.

Any possible solution you can provide?

In our internal plugins we decided to keep two separate binaries, one for Jira 7.x and one for Jira 8.x. Alternatively you could experiment with building a bridge inside your plugin, which I shortly mentioned here: https://youtu.be/tW1nC6f4iU0?t=28m40s. I’m sorry for the inconvenience that comes with a platform release.

Hope that helps,
Kamil Cichy


#6

Hi,

yes, I refer to the same policy and I read the allowed “exception” regarding x.0 Versions.

Anyway, we found already an acceptable solution.

The base problematic is not solved. If Atlassian introduces such changes without giving the plugin developers a chance to figure out a solution it is quite hard to prepare plugins in time. We would appreciate to get either already access to JavaDoc API (even if it is not stable) before the release of a new major version. Second is: Is there any possibility to get access to Atlassian Jira 8 SDK prior Atlassian releases a new major version (e.g. usable pom entry for Jira 8 EAP versions)? This would help us a lot.

I don’t know how we should have been able to figure out that SearchResults.getResults() solves the issue without your comment (many thanks for this!).

Would appreciate to get access to any additional source helping us to prepare faster. Probably it is available in a place we are not aware of.

Thank you!


#7

Hi,

i decided to share our solution for the issue. Maybe Atlassian wants to add “our” solution for solving the issue:

                            // provided by iConcept GmbH, Reinhard Lopinski

try {
// issues = searchResult.getIssues();

			Method newGetMethod = null; 
			
			try {
				newGetMethod = SearchResults.class.getMethod("getIssues");
			} catch (NoSuchMethodException e) {
				try {
					if (logger.isInfoEnabled()) {
						logger.info("SearchResults.getIssues does not exist - trying to use getResults!");
					}
					newGetMethod = SearchResults.class.getMethod("getResults");
				} catch (NoSuchMethodError e2) {
					logger.error("SearchResults.getResults does not exist!");
				}
			}
			
			if (newGetMethod != null) {
				issues = (List<Issue>) newGetMethod.invoke(searchResult);
			} else {
				logger.error("ERROR NO METHOD TO GET ISSUES !");
				throw new RuntimeException("ICT: SearchResults Service from JIRA NOT AVAILABLE (getIssue / getResults)");
			}
			
			
		} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			logger.error("Jql Helper can net get search result (ICT)", e);
		} catch (Exception e) {
			logger.error("Jql Helper can net get search result - other exception (ICT)", e);
		}

#8

Hi,
I’m glad you found a solution.

If Atlassian introduces such changes without giving the plugin developers a chance to figure out a solution it is quite hard to prepare plugins in time. We would appreciate to get either already access to JavaDoc API (even if it is not stable) before the release of a new major version.

As I wrote before, we document all changes in Preparing for Jira 8.0 and its subpages. The getIssues() method that was renamed is mentioned here.

Is there any possibility to get access to Atlassian Jira 8 SDK prior Atlassian releases a new major version (e.g. usable pom entry for Jira 8 EAP versions)?

Please observe the Atlassian Developer blog, where all EAPs are announced. Each announcement also mentions Maven milestone. For example, 8.0 EAP04 is accompanied by milestone 8.0.0-m0012 that you can use in your pom.
Since EAP03 we also publish sources through maven, so you can read the javadoc there.


#9

I am new to this, but having a similar issue. My post functions are failing when I have a jql query in them. I want to try you solution, but do not know where to add it. Where would I insert the above code?


#10

HI,

this is the complete fragment:
SearchResults searchResult = null;

	try {
		Query queryToExecute = JqlQueryBuilder.newBuilder(jqlQuery).buildQuery();
		// Query theQuery = parseResult.getQuery();

		// get results for the jql query
		// searchResult = searchProvider.search(queryToExecute, currUser, PagerFilter.getUnlimitedFilter());

		searchResult = searchService.search(currUser, queryToExecute, PagerFilter.getUnlimitedFilter());
		
		// THIS IS THE OLD CODE - only running against jira 7.x
		// issues = searchResult.getIssues();
		try {
			// issues = searchResult.getIssues();
			
			Method newGetMethod = null; 
			
			try {
				newGetMethod = SearchResults.class.getMethod("getIssues");
			} catch (NoSuchMethodException e) {
				try {
					if (logger.isInfoEnabled()) {
						logger.info("SearchResults.getIssues does not exist - trying to use getResults!");
					}
					newGetMethod = SearchResults.class.getMethod("getResults");
				} catch (NoSuchMethodError e2) {
					logger.error("SearchResults.getResults does not exist!");
				}
			}
			
			if (newGetMethod != null) {
				issues = (List<Issue>) newGetMethod.invoke(searchResult);
			} else {
				logger.error("ERROR NO METHOD TO GET ISSUES !");
				throw new RuntimeException("ICT: SearchResults Service from JIRA NOT AVAILABLE (getIssue / getResults)");
			}
			
			
		} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			logger.error("Jql Helper can net get search result (ICT)", e);
		} catch (Exception e) {
			logger.error("Jql Helper can net get search result - other exception (ICT)", e);
		}

You need to remove all calls to the method searchResult.getIssue(); and replace it by the code above (see where List issues = searchResult.getIssues(); is commented. What is done: Code checks, if getIssues method exists. If yes, the method is used on the searchResult (of type SearchResults). If not found, it tries to get the method getResults on searchResult (of type SearchResults). If found, this method is used. Invocation is done on the current object (the instance) (searchResult - type of SearchResults). So it is determining available methods during runtime. No need to have different implementations for Jira 7.x and 8.x :-).


#11

I get it. Thank you!


#12

@kcichy

I’d like to encourage Atlassian to consider to keep the “getIssues()” method in the SearchResults, given that this is marked as @PublicApi, and it would make it much easier for plugin developers in supporting Jira 7 and Jira 8 without complex workarounds. This method was not marked deprecated in Jira 7.12.0, nor was it’s replacement made available.

Kind regards,
Benedikt Bjarni Bogason

  • Tempo

#13

Considering the API Policy of Jira:

“In general, it may not be possible to create a single plugin artifact that is compatible with different major release families.”

Considering this (in the hard way), usage of API across major release changes of Jira may result in throwing away the complete API of the previous major version and to introduce a totally new API.

This is - my interpretation - not the intention of Atlassian. The result would be, that no plugins (with serious functionality) would be available in the new Jira release. I think (and this is our interpretation), that the API policy is a strict reminder (as far as possible and feasible) for Atlassian to keep an eye on seamless plugin consistency across major Jira versions as well.

Plugin developer requirements (assumption):
To ensure quality and functionality of (non Jira) Plugins, developers would like to have the chance to see (better test) compatibility even across major Jira releases. From our point of view, it should be possible to build a single plugin version which can be deployed on any Jira Version which has not reached end of life.

This does NOT mean, that the plugin (code) can be left unchanged while Atlassian APIs changes as this would NOT allow Atlassian to improve the framework itself. But - and this is important - Atlassian schould (consider) to provide a compile time available solution usable by the plugin across the all framework versions which have not reached EOL.

The problem in the given case is, that :

  • the removed method provides core functionality to the most important business object in Jira (the issue!)
  • there is no compile time checked solution available supported in 7.x and 8.x

The solution which has been posted here already works. This is the good news. The bad news is: The solution can not be treated as “good” as it relies on reflection. As a result (for the future) usage (better dependency) can not be checked any longer during compile time. A possible failure can only be detected during runtime (e.g. using a test) which is in contrast of using a type safe implementation language (Java) in combination with a proposed API.

At the end of the day reliability of the API decreases and might result in lower stability of provided plugins for the Atlassian framework.

Conclusion
We - as plugin developers - have to deal with the decisions taken by Atlassian. But - here - I take the chance to vote for a solution to be provided by Atlassian considering our concerns regarding the above issue. I am convinced, that our arguments are good enough to be considered in Jira 8.x. Atlassian may even put our solution in a Util class to be further maintained by Atlassian to allow seamless ongoing plugin usage and development in this case.

Comments and feedback to our thoughts are highly appreciated.

Kind regards,

Reinhard Lopinski


#14

We considered your request carefully, but eventually decided not to restore the old method. This is not the only API change we did. To be consistent, we’d need to be compatible on all methods across Jira 7 and 8, but then we could call it Jira 7.14. We’re taking the opportunity window to improve the API without having to maintain backwards compatibility. Every bridge, util and so on needs to be written and supported. Our resources are finite, so if we work on these, we don’t work on what we believe is crucial to Jira’s success - it’s functionality and performance. That is not to say we underestimate the value of healthy apps ecosystem, but we think the API choices we made will not have tremendous impact on the plugins.

We advise against using reflection. It goes around compile time checks, increasing code debt.

The model we adopted is compiling two separate plugin versions for Jira 7 and 8. This is, after all, a platform release, with many changes, both in the backend and frontend. Each plugin must be tested with both 7 and 8 anyway.

An alternative solution is to build your own abstraction layer with two submodules compiled against Jira 7 and 8. Then your business logic is kept in one place and Jira interaction layer has two implementations.
While it is feasible for a plugin to do, because it only needs a very specific subset of functionality, we decided not to do that at the Jira level, because we’d need to abstract the whole Lucene (and more).

I hope you’ll understand our motivation.


#15

Hi,

Thank you for taking the time to review the issue. We can understand the reasons (and motivation) for your decision.
So, let’s leave it for now…
One last thing:
Latest available documentation I found is for 7.12.3 (for the issue: https://docs.atlassian.com/software/jira/docs/api/7.12.3/index.html?com/atlassian/jira/issue/search/SearchResults.html). The method getIssues is still NOT marked as deprecated.

Would you please be so kind to inform the responsible guys to

  • add @Deprecated to getIssues (eventually with a comment to use getResults()
  • review, if this (marking as “deprecated”) is done for the other classes and methods which are not going to be supported in the Jira 8.0.x API

This would reflect responsible behavior and will reduce - at least for some plugin developers - unnecessary waste of time.

br,

Reinhard


#16

But getIssues is not deprecated in Jira 7.x. We cannot mark it as deprecated, because there is no replacement for it. It is the right method to call in the Jira 7.x world. getResults appears only in 8.0 and we are not going to backport it to Jira 7.x.

Our attempt to avoid “unnecessary waste of time” is to clearly document all the breaking changes in the Preparing for Jira 8.0 and Lucene upgrade documents. We expect each vendor to read through these. Anyone who makes the effort to read it won’t be surprised by any breaking change.


#17

Hi,

I wonder myself, if there is a misunderstanding on my side…

From your API policy: “Any JIRA API classes or methods that exist but are deprecated in a particular release will continue to exist and work in the following minor releases, but will be removed in the next major release.”

If I understand your comment correctly, than SearchResult.getIssues() is removed in Jira 8.x, but not in Jira 7.x. So, if you follow your API, you have to mark it as deprecated (in 7.x already). There is no need - according to your policy - to offer a solution (see one of my previous posts). Do you agree? Or do i miss something here? Marking a type / method as “deprecated” does not mean, that it is removed rather than means it can be removed any time in future.

If I understand your API correctly, than you use marking as deprecated to inform developers to get prepare for upcoming changes (which is good!). At the end of day, the question is whether you want to follow the “positive way for developers” or the less nice way by appointing to your policy here (“Normally, we will attempt to mark the method or class as deprecated in a previous minor release, but this is not guaranteed.”).

Going through the changes for Jira 8.x I was not able to find any the mentioned issue either.

So, am I still missing something? Or is this just a documentation issue at the moment?

To be honest: Atlassian documentation improved a lot over the last 24 month. Anyway, there is still (and this will never change) enough space for improvement…

Please let me know, if I did not get something in the right way.

Thank you :slight_smile:


#18

I read this API policy as:
IF there is something in 7.x THEN it will be there in 7.(x+1).

This does not imply:
IF something will be removed in 8.0 THEN it had been deprecated in 7.x.

As we develop Jira, we find better ways to do things and then we deprecate the old ones. We mark them as inferior ways to do things, but they keep on working, because we keep backwards compatibility along minor releases. But, every few years we need to break that backwards compatibility to move forward, refresh code base and so on. We can remove the deprecated methods for things that we have already improved in 7.x, easy peasy. But there are many things we improve while working on 8.0. If we followed your proposal, we’d have to release an artificial 7.14 with all the deprecations, or we’d have to know in advance every single change we want to do in 8.0 at the point of releasing 7.13. This would mean much less flexibility for us and, in the end, worse API for you and our users, as we’d get stuck with all the deprecations and much slower pace of change.

We try to address the pains by releasing EAPs as soon as we have something working.


#19

Is there any option is available with out reflection ?

Ref.

try {
Query queryToExecute = JqlQueryBuilder.newBuilder(jqlQuery).buildQuery();
// Query theQuery = parseResult.getQuery();

	// get results for the jql query
	// searchResult = searchProvider.search(queryToExecute, currUser, PagerFilter.getUnlimitedFilter());

	searchResult = searchService.search(currUser, queryToExecute, PagerFilter.getUnlimitedFilter());
	
	// THIS IS THE OLD CODE - only running against jira 7.x
	// issues = searchResult.getIssues();
	try {
		// issues = searchResult.getIssues();
		
		Method newGetMethod = null; 
		
		try {
			newGetMethod = SearchResults.class.getMethod("getIssues");
		} catch (NoSuchMethodException e) {
			try {
				if (logger.isInfoEnabled()) {
					logger.info("SearchResults.getIssues does not exist - trying to use getResults!");
				}
				newGetMethod = SearchResults.class.getMethod("getResults");
			} catch (NoSuchMethodError e2) {
				logger.error("SearchResults.getResults does not exist!");
			}
		}
		
		if (newGetMethod != null) {
			issues = (List<Issue>) newGetMethod.invoke(searchResult);
		} else {
			logger.error("ERROR NO METHOD TO GET ISSUES !");
			throw new RuntimeException("ICT: SearchResults Service from JIRA NOT AVAILABLE (getIssue / getResults)");
		}
		
		
	} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
		logger.error("Jql Helper can net get search result (ICT)", e);
	} catch (Exception e) {
		logger.error("Jql Helper can net get search result - other exception (ICT)", e);
	}

#20

Hi,

what we learned from the whole conversation, the answer is === > no.

There was a clear statement, that there won’t be a compile time supported cross version solution for plugin developers provided by Atlassian. Clear, explicit and definitely.

You need either base your code on reflection or need to build separate versions for Jira 7.x and 8.x.

We - as plugin developer - do not have any concerns using reflection for this limited issue. Code sketch provided (just a blue print) should be placed in a separate package to keep your code base as clean as possible. You may add startup checks there or additional tests to get rid of this part once you stop supporting Jira 7.x versions and / or to get some warnings to be reminded on your work around.