PluginLicenseManager failed to use with REST endpoint

Dear Dev-Community,

With SDK (v.8.2.7) I created a new Jira plugin with a rest module. It has a simple single endpoint called “dummy” (GET /dummy) in class Dummy
When calling, it just returns a 200 - works pretty fine.

Then I followed https://developer.atlassian.com/platform/marketplace/adding-licensing-support-to-server-apps/#step-3--make-your-app-licensable and modified pom.xml and atlassian-plugin.xml.

Like in step 4 described, I extended my Dummy class to validate the plugin license:

import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import com.atlassian.upm.api.license.PluginLicenseManager;

@Scanned
@Path("/dummy")
public class Dummy {
	
	@ComponentImport
	final private PluginLicenseManager pluginLicenseManager;
	
	@Inject
	public Dummy(PluginLicenseManager pluginLicenseManager) {
		this.pluginLicenseManager = pluginLicenseManager;
	}

	@GET
	@Produces({MediaType.APPLICATION_JSON})
	public Response getDummy() {
		try {
			if (pluginLicenseManager.getLicense().isDefined()) {
	            return Response.ok(pluginLicenseManager.getLicense().get().getRawLicense()).build();
	        } else {
	        	ResponseBuilder missing = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
	        	missing.entity("License missing!");
	            return missing.build();
	        }
		} catch (Exception e) {
        	ResponseBuilder exc = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
        	exc.entity(e.toString());
            return exc.build();
		}
	}	
}

I applied a 3h timebomb license. When called the modified endpoint, I get the following error:

2021-07-15 21:03:30,095+0200 http-nio-2990-exec-10 ERROR admin 1263x9613x1 1vimlvu 0:0:0:0:0:0:0:1 /rest/myapi/1/dummy [c.a.plugin.servlet.DefaultServletModuleManager] Unable to create new reference LazyLoadedFilterReference{descriptor=......jira.rest....:restext-filter (), filterConfig=com.atlassian.plugin.servlet.filter.PluginFilterConfig@7f95f813}
io.atlassian.util.concurrent.LazyReference$InitializationException: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '........rest.Dummy': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.atlassian.upm.api.license.PluginLicenseManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at io.atlassian.util.concurrent.LazyReference.getInterruptibly(LazyReference.java:156)
	at io.atlassian.util.concurrent.LazyReference.get(LazyReference.java:116)
	at com.atlassian.plugin.servlet.DefaultServletModuleManager.getInstance(DefaultServletModuleManager.java:430)
	at com.atlassian.plugin.servlet.DefaultServletModuleManager.getFilter(DefaultServletModuleManager.java:423)
	at com.atlassian.plugin.servlet.DefaultServletModuleManager.getFilters(DefaultServletModuleManager.java:288)
	at com.atlassian.plugins.rest.module.servlet.DefaultRestServletModuleManager.getFilters(DefaultRestServletModuleManager.java:125)
	... 34 filtered
	at com.atlassian.jira.plugin.mobile.web.filter.MobileAppRequestFilter.doFilter(MobileAppRequestFilter.java:59)
	... 4 filtered
	at com.atlassian.jira.plugin.mobile.login.MobileLoginSuccessFilter.doFilter(MobileLoginSuccessFilter.java:54)
	... 3 filtered
	at com.atlassian.diagnostics.internal.platform.monitor.http.HttpRequestMonitoringFilter.doFilter(HttpRequestMonitoringFilter.java:55)
	... 8 filtered
	at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
	... 24 filtered
	at com.atlassian.labs.httpservice.resource.ResourceFilter.doFilter(ResourceFilter.java:59)
	... 22 filtered
	at com.atlassian.ratelimiting.internal.filter.RateLimitFilter.doFilter(RateLimitFilter.java:73)
	... 17 filtered
	at com.atlassian.jira.security.JiraSecurityFilter.lambda$doFilter$0(JiraSecurityFilter.java:66)
	... 1 filtered
	at com.atlassian.jira.security.JiraSecurityFilter.doFilter(JiraSecurityFilter.java:64)
	... 16 filtered
	at com.atlassian.plugins.rest.module.servlet.RestSeraphFilter.doFilter(RestSeraphFilter.java:38)
	... 19 filtered
	at com.atlassian.jira.servermetrics.CorrelationIdPopulatorFilter.doFilter(CorrelationIdPopulatorFilter.java:30)
	... 10 filtered
	at com.atlassian.ratelimiting.internal.filter.RateLimitPreAuthFilter.doFilter(RateLimitPreAuthFilter.java:71)
	... 3 filtered
	at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
	... 4 filtered
	at com.atlassian.web.servlet.plugin.LocationCleanerFilter.doFilter(LocationCleanerFilter.java:36)
	... 26 filtered
	at com.atlassian.jira.servermetrics.MetricsCollectorFilter.doFilter(MetricsCollectorFilter.java:25)
	... 24 filtered
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name .......rest.Dummy': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.atlassian.upm.api.license.PluginLicenseManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:733)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:198)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:353)

I have no clue what piece of information is missing.

Thanks for any hint
Thomas D

You should not use @Scanned and @Inject but instead use these annotations and move the ComponentImport to the constructor.
Try this:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// ....

@Component
@Path("/dummy")
public class Dummy {

  // JIRA API
  private final PluginLicenseManager pluginLicenseManager;

  @Autowired
  public PluginLicenseService(@ComponentImport PluginLicenseManager pluginLicenseManager) {
		this.pluginLicenseManager = pluginLicenseManager;
  }

I cannot tell you why Scanned and Inject is bad, but over a year ago I refactored all my code to Component+Autowired. I thinks once the spring-scanner dependency got updated I think.

Hope you can get it running.

Cheers,

Bernhard

I created a new plugin from scratch with the SDK and merged step by step code and configuration items from my original project. The root cause was this META-INF/spring/plugin-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- Package with Spring configuration -->
    <context:component-scan base-package="com.my.plugins.jira.config" />
</beans>

I replaced it with this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:atlassian-scanner="http://www.atlassian.com/schema/atlassian-scanner/2"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.atlassian.com/schema/atlassian-scanner
        http://www.atlassian.com/schema/atlassian-scanner/atlassian-scanner.xsd">
    <atlassian-scanner:scan-indexes/>
</beans>

This file was never modified by myself - so I assume the changes came from SDK. Additionally this was missing in the pom.xml:

            <plugin>
                <groupId>com.atlassian.plugin</groupId>
                <artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
                <version>${atlassian.spring.scanner.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>atlassian-spring-scanner</goal>
                        </goals>
                        <phase>process-classes</phase>
                    </execution>
                </executions>
                <configuration>
                    <verbose>false</verbose>
                </configuration>
            </plugin>

So long
Thomas