Jira 11.x Plugin Migration – Unable to Inject services

Hello everyone,

We are currently migrating our legacy Data Center plugin to Jira 11.x, adapting it to: Jakarta (jakarta.*), Updated Spring versions, Removal of legacy javax.*, modern plugin framework changes ..

However, we are stuck on a dependency injection issue when accessing (for example) one of our servlets.
When accessing our servlet (used for initial addon configuration), Jira throws:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'com.sykorait.vmcon.ConnectorSettingsServlet': 
Unsatisfied dependency expressed through constructor parameter 0: 

No qualifying bean of type 'com.atlassian.sal.api.user.UserManager' available: 
expected at least 1 bean which qualifies as autowire candidate.

No matter what combinations we try, we are not able to import and inject these components (UserManager, ApplicationProperties, ..)

In previous Jira versions (javax-based, older Spring), this worked correctly using: @ComponentImport, @Inject, @Scanned ..

We also tried what seems to be the now recommended JavaConfig-based OSGi approach using:

pom.xlm

<?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.sykorait.jira.plugins</groupId>
    <artifactId>USU-Service-Management-Connector</artifactId>
    <version>2.2.3</version>
    <organization>
        <name>Sykora IT s.r.o.</name>
        <url>https://www.sykorait.com</url>
    </organization>
    <name>USU Service Management Connector for Jira</name>
    <description>Integration between USU Service Management (Valuemation) and Jira.</description>
    <packaging>atlassian-plugin</packaging>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-core</artifactId>
                <version>2.3.0.1</version>
            </dependency>

            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-runtime</artifactId>
                <version>4.0.2</version>  
            </dependency>
     
            <dependency>
                <groupId>com.sun.istack</groupId>
                <artifactId>istack-commons-runtime</artifactId>
                <version>4.0.0</version>
            </dependency>

            <dependency>
                <groupId>com.sun.xml.fastinfoset</groupId>
                <artifactId>FastInfoset</artifactId>
                <version>1.2.17</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <!-- Jira 11.x changes-->
<dependency>
            <groupId>com.atlassian.plugins</groupId>
            <artifactId>atlassian-plugins-osgi-javaconfig</artifactId>
            <version>${osgi.javaconfig.version}</version>
        </dependency>
        <dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>com.sun.activation</groupId>
    <artifactId>jakarta.activation</artifactId>
    <version>1.2.2</version>
    <scope>provided</scope>
</dependency>

        <dependency>
    <groupId>commons-httpclient</groupId>
    <artifactId>commons-httpclient</artifactId>
    <version>3.1</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>jakarta.activation</groupId>
    <artifactId>jakarta.activation-api</artifactId>
    <version>2.1.1</version>
    <scope>provided</scope>
</dependency>

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.10-atlassian-2</version> 
            <scope>provided</scope>               
        </dependency>
       
        <dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>

        
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>4.0.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>jakarta.ws.rs</groupId>
            <artifactId>jakarta.ws.rs-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>jakarta.inject</groupId>
            <artifactId>jakarta.inject-api</artifactId>
            <version>2.0.1</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>com.atlassian.applinks</groupId>
            <artifactId>applinks-api</artifactId>
            <version>8.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.renderer</groupId>
            <artifactId>atlassian-renderer</artifactId>
            <version>8.0.5</version>
            <scope>provided</scope>
            <exclusions>
        
    </exclusions>
        </dependency>

        <dependency>
            <groupId>com.atlassian.templaterenderer</groupId>
            <artifactId>atlassian-template-renderer-api</artifactId>
            <version>5.0.2</version>
            <scope>provided</scope>
            <exclusions>
       
    </exclusions>
        </dependency>
        
        <dependency>
            <groupId>com.google.collections</groupId>
            <artifactId>google-collections</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.activeobjects</groupId>
            <artifactId>activeobjects-plugin</artifactId>
            <version>${ao.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>licensing-api</artifactId>
            <version>5.1.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>upm-api</artifactId>
            <version>5.1.2</version>
            <scope>provided</scope>
        </dependency>
		
		
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-api</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
			<exclusions>
                
                <exclusion>
                    <groupId>jta</groupId>
                    <artifactId>jta</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-core</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
			<exclusions>
			  <exclusion>
				 <groupId>jndi</groupId>
				 <artifactId>jndi</artifactId>
			  </exclusion>
              <exclusion>
            <groupId>org.opensaml</groupId>
            <artifactId>opensaml-saml-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.opensaml</groupId>
            <artifactId>opensaml-saml-impl</artifactId>
        </exclusion>
            <exclusion>
                <groupId>jta</groupId>
                <artifactId>jta</artifactId>
            </exclusion>
			</exclusions>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.plugin</groupId>
            <artifactId>atlassian-spring-scanner-annotation</artifactId>
            <version>${atlassian.spring.scanner.version}</version>
            <scope>provided</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>com.atlassian.plugins</groupId>
            <artifactId>atlassian-plugins-osgi-testrunner</artifactId>
            <version>${plugin.testrunner.version}</version>
            <scope>test</scope>
        </dependency>
		
       
        <dependency>
			<groupId>com.atlassian.plugins.rest</groupId>
			<artifactId>atlassian-rest-v2-api</artifactId>
			<version>8.0.6</version>
			<scope>provided</scope>
		</dependency>
        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-api</artifactId>
            <version>7.2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.wink</groupId>
            <artifactId>wink-client</artifactId>
            <version>1.4</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>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>plugin-license-storage-lib</artifactId>
            <version>${upm.license.compatibility.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>plugin-license-storage-plugin</artifactId>
            <version>${upm.license.compatibility.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
            <scope>compile</scope>
        </dependency>
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo.maven.apache.org/maven2/</url>
        </repository>
        <repository>
            <id>atlassian-public</id>
            <url>https://packages.atlassian.com/maven-public/</url>
            </repository>
    </repositories>
    <pluginRepositories>
    <pluginRepository>
        <id>atlassian-public</id>
        <url>https://packages.atlassian.com/maven-public/</url>
    </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>jira-maven-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <!--<applications>
                        <application>
                            <applicationKey>jira-servicedesk</applicationKey>
                            <version>${jira.servicedesk.application.version}</version>
                        </application>
                    </applications>-->
                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <!-- Uncomment to install TestKit backdoor in JIRA. -->
                    <!--
                    <pluginArtifacts>
                        <pluginArtifact>
                            <groupId>com.atlassian.jira.tests</groupId>
                            <artifactId>jira-testkit-plugin</artifactId>
                            <version>${testkit.version}</version>
                        </pluginArtifact>
                    </pluginArtifacts>
                    -->
                    <enableQuickReload>true</enableQuickReload>
                    <!-- 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.sykorait.jira.plugins.vmconnector.api,</Export-Package>
                        <!-- Add package import here -->
                        <Import-Package>
                            !javax.*,
                            jakarta.servlet*;version="[6.0.0,7.0.0)",
                            jakarta.ws.rs*;version="[3.0.0,4.0.0)",
                            jakarta.inject*;version="[2.0.0,3.0.0)",
                            jakarta.annotation*;version="[2.0.0,3.0.0)",
                            jakarta.xml.bind*;version="[4.0.0,5.0.0)",
                            com.atlassian.jira.plugin.webfragment.conditions,
                            org.springframework.osgi.*;resolution:="optional",
                            org.eclipse.gemini.blueprint.*;resolution:="optional",
                            com.atlassian.upm.*;resolution:="optional",
                            *
                        </Import-Package>
                        <!-- Ensure plugin is spring powered -->
                        <Spring-Context>*</Spring-Context>
                        <Private-Package>com.atlassian.upm.license.storage.lib*</Private-Package>
                        <!-- <DynamicImport-Package>com.atlassian.upm.api.license.entity;version="2.0.1", com.atlassian.upm.api.license;version="2.0.1", com.atlassian.upm.api.util;version="2.0.1", com.atlassian.upm.license.storage.plugin;version="${upm.license.compatibility.version}"</DynamicImport-Package>-->
                    </instructions>
                    <bundledArtifacts>
                        <bundledArtifact>
                            <groupId>com.atlassian.upm</groupId>
                            <artifactId>atlassian-universal-plugin-manager-plugin</artifactId>
                            <version>5.1.2</version>
                        </bundledArtifact>
                        <bundledArtifact>
                            <groupId>com.atlassian.upm</groupId>
                            <artifactId>plugin-license-storage-plugin</artifactId>
                            <version>${upm.license.compatibility.version}</version>
                        </bundledArtifact>
                    </bundledArtifacts>
                </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>
                    <includeExclude>-com.atlassian.plugin.spring.scanner.annotation.*</includeExclude>
                    <scannedDependencies>
                        <dependency>
                            <groupId>com.atlassian.plugin</groupId>
                            <artifactId>atlassian-spring-scanner-external-jar</artifactId>
                        </dependency>
                    </scannedDependencies>
                    <verbose>false</verbose>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-storage-plugin</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
                            <includeArtifactIds>plugin-license-storage-plugin</includeArtifactIds>
                            <stripVersion>true</stripVersion>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!--
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>7.0.4</version>
                <configuration>
                    <skipProvidedScope>true</skipProvidedScope>
                    <suppressionFiles>
                        <suppressionFile>https://dcapt-downloads.s3.amazonaws.com/atlassian-security-scanner-dc-apps-suppressions.xml</suppressionFile>
                    </suppressionFiles>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            -->

        </plugins>
    </build>
    <properties>
        <ao.version>6.1.2</ao.version>
       <jira.version>11.3.0</jira.version>
        <amps.version>9.9.1</amps.version>
        <osgi.javaconfig.version>0.6.0</osgi.javaconfig.version>
        <atlassian.spring.scanner.version>3.0.3</atlassian.spring.scanner.version>  <!-- proven stable -->
        <spring.version>5.3.39-atlassian-3</spring.version>
        <jira.servicedesk.application.version>4.15.0 </jira.servicedesk.application.version>
        <plugin.testrunner.version>2.0.1</plugin.testrunner.version>
        <!-- This property ensures consistency between the key in atlassian-plugin.xml and the OSGi bundle's key. -->
        <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
        <!-- TestKit version 6.x for JIRA 6.x -->
        <testkit.version>6.3.11</testkit.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- Jira 11.x changes-->
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <maven.compiler.release>21</maven.compiler.release>
        <upm.license.compatibility.version>2.15.3</upm.license.compatibility.version>
    </properties>
</project>

servlet:

package com.sykorait.vmcon;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.jira.util.json.JSONException;
import com.atlassian.jira.util.json.JSONObject;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.sal.api.auth.LoginUriProvider;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.templaterenderer.TemplateRenderer;
import com.atlassian.upm.api.license.PluginLicenseManager;
import com.atlassian.upm.api.license.entity.PluginLicense;

import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;

@Named
public class ConnectorSettingsServlet extends HttpServlet {

    private static final String TEMPLATE = "templates/connector-settings.vm";

  
    private final PluginLicenseManager pluginLicenseManager;
    private final TemplateRenderer templateRenderer;
    private final UserManager userManager;
    private final LoginUriProvider loginUriProvider;
    private final ActiveObjects ao;
    private final ApplicationProperties applicationProperties;

    public ConnectorSettingsServlet(UserManager userManager, ApplicationProperties applicationProperties, LoginUriProvider loginUriProvider, PluginLicenseManager pluginLicenseManager, TemplateRenderer templateRenderer, ActiveObjects ao) {
        this.pluginLicenseManager = pluginLicenseManager;
        this.templateRenderer = templateRenderer;
        this.userManager = userManager;
        this.loginUriProvider = loginUriProvider;
        this.applicationProperties = applicationProperties;
        this.ao = ao;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        Map<String, Object> context = new HashMap<>();
        context.put("baseUrl", applicationProperties.getBaseUrl());
        res.setContentType("text/html;charset=utf-8");

        if (pluginLicenseManager.getLicense().isDefined()) {
          .....
        }
    }

    private void redirectToLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.sendRedirect(loginUriProvider.getLoginUri(getUri(request)).toASCIIString());
    }

    private URI getUri(HttpServletRequest request) {
        StringBuffer builder = request.getRequestURL();
        if (request.getQueryString() != null) {
            builder.append("?").append(request.getQueryString());
        }
        return URI.create(builder.toString());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        ... 
    }
}

JavaConfig (following the attlassian myPlugin tutorial)

package com.sykorait.vmcon;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.sal.api.auth.LoginUriProvider;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.templaterenderer.TemplateRenderer;
import com.atlassian.upm.api.license.PluginLicenseManager;
import com.atlassian.plugins.osgi.javaconfig.ExportOptions;
import com.atlassian.plugins.osgi.javaconfig.configs.beans.ModuleFactoryBean;
import com.atlassian.plugins.osgi.javaconfig.configs.beans.PluginAccessorBean;
import org.osgi.framework.ServiceRegistration;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import jakarta.servlet.http.HttpServlet;

import static com.atlassian.plugins.osgi.javaconfig.OsgiServices.exportOsgiService;
import static com.atlassian.plugins.osgi.javaconfig.OsgiServices.importOsgiService;

@Configuration
@Import({ModuleFactoryBean.class, PluginAccessorBean.class})
public class SpringConfig {

    // Import OSGi services
    @Bean
    public ApplicationProperties applicationProperties() {
        return importOsgiService(ApplicationProperties.class);
    }

    @Bean
    public UserManager userManager() {
        return importOsgiService(UserManager.class);
    }

    @Bean
    public LoginUriProvider loginUriProvider() {
        return importOsgiService(LoginUriProvider.class);
    }

    @Bean
    public PluginLicenseManager pluginLicenseManager() {
        return importOsgiService(PluginLicenseManager.class);
    }

    @Bean
    public TemplateRenderer templateRenderer() {
        return importOsgiService(TemplateRenderer.class);
    }

    @Bean
    public ActiveObjects activeObjects() {
        return importOsgiService(ActiveObjects.class);
    }

    // Create servlet using the OSGi services
    @Bean
    public ConnectorSettingsServlet connectorSettingsServlet(
            UserManager userManager,
            ApplicationProperties applicationProperties,
            LoginUriProvider loginUriProvider,
            PluginLicenseManager pluginLicenseManager,
            TemplateRenderer templateRenderer,
            ActiveObjects activeObjects) {

        return new ConnectorSettingsServlet(
                userManager,
                applicationProperties,
                loginUriProvider,
                pluginLicenseManager,
                templateRenderer,
                activeObjects
        );
    }

    // Optional: Export servlet as OSGi service (if needed)
    @Bean
    public FactoryBean<ServiceRegistration> registerConnectorServlet(ConnectorSettingsServlet servlet) {
        return exportOsgiService(servlet, ExportOptions.as(ConnectorSettingsServlet.class));
    }
}

Is there something fundamentally different about how services must be imported or exposed in Jira 11?

@MartinFischer Could you try setting a debug point inside the UserManager @Bean method and see if it’s executed? Also if you’re at Atlas Camp @MarekTokarski and I are at the Product & Design booth this week

Thanks for the suggestion @mkemp I tried to add logging inside the UserManager @Bean method but nothing appears in the Jira logs. I also cannot debug it directly because there isn’t a compatible Atlassian SDK for Jira 11 if I am not mistaken. So it seems the bean method is never actually called during plugin load. I am not really sure what we are missing here.

I believe something similar was already discussed here.

Edit:

<?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-2.5.xsd
        http://www.atlassian.com/schema/atlassian-scanner/2
        http://www.atlassian.com/schema/atlassian-scanner/2/atlassian-scanner.xsd">
    <atlassian-scanner:scan-indexes/>
</beans>

This and @Named, @Inject annotations probably have solved this problem.

@MartinFischer are you sure you got the correct Spring version? Jira 11 upgraded to version 6.2.x RFC-77: Upgrades to Spring and Jakarta versions. We made no change to Spring annotations, and usermanager still imports with @ComponentImport for our services. Not that I think it should actually matter for a provided dependency, but perhaps this in combination with your import-package statements in MANIFEST.mf, this can explain the behavior? Incidentally, what does import-package look like in your bundled manifest?

@MartinFischer From the Spring XML it looks like two parts are missing to use OSGi Java Config

  1. The Java config file is not declared as a bean
  2. Annotation scanning is not enabled (<atlassian-scanner:scan-indexes/> is only for Atlassian Spring Scanner, it is not for Spring)

It should be like:

    <context:annotation-config/>
    <bean class="com.sykorait.vmcon.SpringConfig "/>

See an example in our open source platform: https://bitbucket.org/atlassian/dc-platform/src/b8448ee818c277c6b1eb7fec661abdbe4d0d8490/refapp/refapp-plugins/ao-plugin/src/main/resources/META-INF/spring/spring.xml#lines-8,9

Thank you both @mkemp @EliasBrattliSorensen for the quick and helpful replies!

We managed to get it working using a combination of @Inject, @Component, and @Autowired annotations on our custom services and servlets. @ComponentImport still works fine for us, but we had to move it exclusively to the constructor parameters (field-level @ComponentImport didn’t resolve reliably in this setup).

@mkemp thanks especially for the pointer about the missing <context:annotation-config/> and explicit bean declaration in the Spring XML when using Java config. That makes perfect sense now and is good to know for future cases where we might lean more heavily on OSGi Java Config.

1 Like