Error when wiring class - Confluence REST Plugin

I have come into some issues when trying to properly use UserCountServiceImpl in my rest plugin for confluence.

I am not positive but I believe the correct import in maven is (unless it should be done as a plugin):

<dependency>
           <groupId>com.atlassian.confluence.plugins</groupId>
           <artifactId>confluence-license-rest</artifactId>
           <version>${confluence.version}</version>
</dependency>

References in the .java file:

import com.atlassian.confluence.license.rest.service.UserCountServiceImpl;

(including this doesn’t cause an issue)

private final UserCountServiceImpl userCountServiceImpl = null;

(Including this DOES causes an issue)

I’ve tried to annotate it with @ComponentImport, @Inject and no annotation - every time I try to include it - I get something along the lines of this below when installing the plugin:

Caused by: org.osgi.framework.BundleException: Unresolved constraint in bundle {$groupID} [264]: Unable to resolve 264.0: missing requirement [264.0] osgi.wiring.package; (osgi.wiring.package=com.atlassian.confluence.license.rest.service)

The plugin does builds fine - so I believe I have an issue in the POM file or the annotation that I am using. Or perhaps it’s a completely different issue.

As one last bit of info - I am working on a REST plugin, so I have used the Atlassian Confluence SDK Plugin creation (version 1, not 2) to get me started version.

Below is the rest resource that I am using that includes this class. I don’t have it doing anything just to determine the root cause.

@Path("/message")
public class MyRestResource {

    @ComponentImport
    private final UserManager userManager = null;

    @ComponentImport
    private final UserChecker userChecker = null;

    @ComponentImport
    private final LicenseService licenseService = null;

    @ComponentImport
    private UserCountServiceImpl userCountServiceImpl = null;


    @GET
    @AnonymousAllowed
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getMessage()
    {
       return Response.ok(new MyRestResourceModel("Hello World")).build();
    }
}

Thanks for the help!

Adding this more defined error:
Unable to resolve 270.0: missing requirement [270.0] osgi.wiring.package; (osgi.wiring.package=com.atlassian.confluence.license.rest.service). This error usually occurs when your plugin imports a package from another bundle with a specific version constraint and either the bundle providing that package doesn't meet those version constraints, or there is no bundle available that provides the specified package. For more details on how to fix this, see https://developer.atlassian.com/x/mQAN

I am pretty fresh to plugin development, especially with Atlassian products - I am trying to look at the Felix Web Console now.

Add scope provided to the dependency

Thanks for the response. I have and it still gives me that issue.

As Panos says, make sure that the dependency scope is set to Provided. Spring Scanner annotation based injection usually looks like this (untested). Check out my @Named annotation, the @Inject annotation, and my constructor.

@Named("MyRestResource")
@Path("/message")
public class MyRestResource {

    @ComponentImport private final UserManager userManager;
    @ComponentImport private final UserChecker userChecker;
    @ComponentImport private final LicenseService licenseService;
    @ComponentImport private UserCountServiceImpl userCountServiceImpl;

    @Inject public MyRestResource(UserManager userManager, UserChecker userChecker, LicenseService licenseService, UserCountServiceImpl, userCountServiceImpl){
      this.userManager = userManager;
      this.userChecker = userChecker;
      this.licenseService = licenseService;
      this.userCountServiceImpl = userCountServiceImpl;
    }

    @GET
    @AnonymousAllowed
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getMessage()
    {
       return Response.ok(new MyRestResourceModel("Hello World")).build();
    }
}

Appreciate your input, unfortunately I’m still coming up with the same error. I know you mentioned it was untested so just as a heads up, your code has an extra comma in one of the parameters in the constructor.

That being said… perhaps my POM file is not pulling in the dependency correctly?

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.co.atlassian.conflicchk</groupId>
    <artifactId>conflicchk</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <organization>
        <name>Example Company</name>
        <url>http://www.example.com/</url>
    </organization>
    <name>conflicchk</name>
    <description>This is the com.co.atlassian.conflicchk:conflicchk plugin for Atlassian Confluence.</description>
    <packaging>atlassian-plugin</packaging>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence</artifactId>
            <version>${confluence.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.plugin</groupId>
            <artifactId>atlassian-spring-scanner-annotation</artifactId>
            <version>${atlassian.spring.scanner.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.plugin</groupId>
            <artifactId>atlassian-spring-scanner-runtime</artifactId>
            <version>${atlassian.spring.scanner.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
            <scope>provided</scope>
        </dependency>
        <!-- WIRED TEST RUNNER DEPENDENCIES -->
        <dependency>
            <groupId>com.atlassian.plugins</groupId>
            <artifactId>atlassian-plugins-osgi-testrunner</artifactId>
            <version>${plugin.testrunner.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>jsr311-api</artifactId>
            <version>1.1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.2.2-atlassian-1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.plugins.rest</groupId>
            <artifactId>atlassian-rest-common</artifactId>
            <version>1.0.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-api</artifactId>
            <version>2.6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.wink</groupId>
            <artifactId>wink-client</artifactId>
            <version>1.1.3-incubating</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.8.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.confluence.plugins</groupId>
            <artifactId>confluence-license-rest</artifactId>
            <version>${confluence.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.confluence.plugins</groupId>
                <artifactId>confluence-license-rest</artifactId>
                <version>${confluence.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <productVersion>${confluence.version}</productVersion>
                    <productDataVersion>${confluence.data.version}</productDataVersion>
                    <enableQuickReload>true</enableQuickReload>
                    <enableFastdev>false</enableFastdev>
                    <!-- See here for an explanation of default instructions: -->
                    <!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
                    <instructions>
                        <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
                        <!-- Add package to export here -->
                        <Export-Package>com.co.atlassian.conflicchk.api,</Export-Package>
                        <!-- Add package import here -->
                        <Import-Package>org.springframework.osgi.*;resolution:="optional", org.eclipse.gemini.blueprint.*;resolution:="optional", *</Import-Package>
                        <!-- Ensure plugin is spring powered -->
                        <Spring-Context>*</Spring-Context>
                    </instructions>
                </configuration>
            </plugin>
            <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>
                    <scannedDependencies>
                        <dependency>
                            <groupId>com.atlassian.plugin</groupId>
                            <artifactId>atlassian-spring-scanner-external-jar</artifactId>
                        </dependency>
                    </scannedDependencies>
                    <verbose>false</verbose>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <properties>
        <confluence.version>5.10.7</confluence.version>
        <confluence.data.version>5.10.5</confluence.data.version>
        <amps.version>6.2.11</amps.version>
        <plugin.testrunner.version>1.2.3</plugin.testrunner.version>
        <atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
        <!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. -->
        <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
    </properties>
</project>

It doesn’t appear that com.atlassian.confluence.license.rest.service exports any package. You won’t be able to inject it like this.

Two things you could try:

Add the @Scanned Annotation to your RestResource class

Remove the version element from the dependency and just add scope provided.

The second one solved a lot of problems with Spring Scanner for me the last days.

I think you’re probably correct. I’m not too familiar with injecting dependencies. Would you happen to know any alternative methods?

Thanks… I tried @Scanned before and once again - with no luck. Removing the version element to my POM wound up causing an error as I packaged the plugin - so I left it in. I think @sfbehnke is right on the right track with his last comment.

Mobile typing… Google Atlassian osgi browser and follow instructions to open you server’s osgi browser. Find the Plugin that you want to use its service. Expand. Check the exported services. Do you see the service you want to import?

It is not exported, no. That package has no exports.

Well, you are kind of stuck. Let’s take a step back. What are you trying to achieve?

Edit:
For future reference, you cant wire a concrete class (UserServiceImpl). That’s wrong. You can’t do that, you need to wire the interface.

Update: To fix your error, add com.atlassian.confluence.license.rest.service.*;resolution:="optional",
Although this fix your initial error regarding wiring, this still doesn’t work as you take now class not found exception.

So, let’s take a peak in the source code of the UserCountServiceImp for confluence 5.8.8:

public UserCountServiceImpl(UserChecker userChecker, LicenseService licenseService) {

To construct this we need the UserChecker (given by confluence) and the LicenseService (given again by confluence).
So, we actually need nothing from that plugin in order to make the functions exposed by the interface

Integer getUserCount();
Optional<Integer> getRemainingFreeSlots();
Integer getLicenseMaxUsers();

work other than the logic itself.

The last piece, the logic; can be found by checking source code in the plugin’s class files
https://www.mvnrepository.com/artifact/com.atlassian.confluence.plugins/confluence-license-rest

Hope it helps,
Panos

Great. Thank you for your help! I was thinking the wiring of the concrete class was an issue - but I am going behind someone else’s code so I trusted their judgement instead. I’ll try working with this over the weekend and see if I can get it working. I’ll post my results later.

Ah ha! I see what you’re saying! UserCountServiceImpl doesn’t provide much that cannot be recreated with the two classes I already have imported (which are provided with the basic plugin).

Very clever! I got so worked up on thinking I was creating this plugin incorrectly that I didn’t take the time to think of an alternative approach.

Thank you all for your help!

Update: As a follow up, this worked perfectly!

2 Likes

Could you share working configuration of the your code?
We have similar problem with you.