Last updated Jan 29, 2025

Spring Java configuration - converting from Spring XML

This page explains how to convert your P2 app to use Spring Java configuration, if it currently declares its components in .xml files under src/main/resources/META-INF/spring.

Reference documentation

POM changes

  1. Add the necessary dependencies.
  2. Add the <Atlassian-Plugin-Key> header to your AMPS <instructions>, to indicate that your plugin should not be transformed at runtime into an OSGi bundle. For example:
1
2
<project ...>
  <!-- ... -->
  <build>
    <plugins>
      <plugin>
        <groupId>com.atlassian.maven.plugins</groupId>
        <artifactId>amps-maven-plugin</artifactId>
        <!-- Or jira-maven-plugin, etc. -->
        <!-- For AMPS pre-8.0, use maven-amps-plugin, maven-jira-plugin, etc. -->
        <version>9.1.11</version>
        <configuration>
          <instructions>
            <!-- You may want to extract the plugin key to a POM property, for reuse elsewhere -->
            <Atlassian-Plugin-Key>my-plugin-key</Atlassian-Plugin-Key>
            <!-- You may need to import some packages whose usage is not detected by the maven-bundle-plugin, or
                 mark some packages as optional, or add version ranges to them, etc. -->
            <Import-Package>*</Import-Package>
            <!-- Ensure the plugin is Spring-powered -->
            <Spring-Context>*</Spring-Context>
            <!-- ... -->
          </instructions>
          <!-- ... -->
        </configuration>
      </plugin>
      <!-- ... -->
    </plugins>
  </build>
  <!-- ... -->
</project>

Create a Java config class

  1. Create a class for your Spring config, called (for example) SpringBeans, in a new package somewhere under src/main/java, for example com.example.myplugin.spring.
  2. Annotate that class with @org.springframework.context.annotation.Configuration.

Change your Spring XML

You will currently have at least one Spring XML file under src/main/resources/META-INF/spring (it could be called anything ending in .xml). Modify this file as follows:

  1. Remove any mentions of the osgi namespace from the root element.
  2. Remove any mentions of Spring DM or Gemini Blueprint from xsi:schemaLocation
  3. Replace any usages of the osgi namespace with equivalent @Bean methods in your Java config class.
  4. Register your new @Configuration classes.
  5. Convert vanilla Spring XML to Spring Java. Spring documentation

Importing services from OSGi

For each service you’re importing via an osgi:reference element in your Spring XML files (if any) which would look like:

1
2
<osgi:reference id="foo">
    <osgi:interfaces>
        <beans:value>com.example.FooService</beans:value>
    </osgi:interfaces>
</osgi:reference>

Replace it with a method like this in your Spring config class:

1
2
@Bean
public FooService fooService() {
    return OsgiServices.importOsgiService(FooService.class);
}

Filtering

Note that since version 0.2.0 of this helper library, you can optionally specify an LDAP filter, just as you could add a filter attribute to <component-import>. Here's an example that works with 0.3.1 and later:

1
2
@Bean
public FooService fooService() {
    return importOsgiService(FooService.class, ImportOptions.defaultOptions().withFilter("(some.key=some.value)"));
}

Here, the FooService will be imported only if it has a some.key property with the value some.value.

Service collections

Since 0.6.0 it's possible to replace importing a collection of OSGi services (i.e. using osgi:list or osgi:set) in XML like:

1
2
<osgi:list id="fooServices" interface="com.example.FooService" cardinality="0..N" />

The equivalent with Java config is:

1
2
@Bean
public List<FooService> fooServices() {
    return OsgiServices.importOsgiServiceCollection(list(FooService.class));
}

Exporting services to OSGi

In Spring XML, exporting an existing bean to an OSGi service looked like:

1
2
<bean id="foo" class="com.example.FooService" />
<osgi:service id="foo_osgiService" ref="foo">
  <osgi:interfaces>
    <value>com.example.FooService</value>
    <value>com.example.BarService</value>
  </osgi:interfaces>
  <osgi:service-properties>
    <beans:entry key="baz" value="qux"/>
  </osgi:service-properties>
</osgi:service>

where each interface is what the service is exposed to OSGi with and where the ref attribute refers to the existing bean name to expose as an OSGi service.

Replace it like so:

1
2
@Bean
public FooService foo() {
    return new FooService();
}

@Bean
public FactoryBean<ServiceRegistration> exportFooService(FooService foo) {
    return OsgiServices.exportOsgiService(foo, ExportOptions.as(FooService.class, BarService.class).withProperty("baz", "qux"));
}

Defining plugin module types

If the exported service was a module type where the interface was ListableModuleDescriptorFactory.class there is shorthand for this:

1
2
@Bean
public FactoryBean<ServiceRegistration> exportMyModuleType(
        final ListableModuleDescriptorFactory moduleDescriptorFactory) {
    return OsgiServices.exportAsModuleType(moduleDescriptorFactory);
}

Decomposing your config classes [optional]

If your @Configuration class has become too big or messy, you can easily split it up as follows:

  1. Pick one of your config classes (or create a new config class) to be the “main” config class.
  2. Move some/all of its beans to one or more new @Configuration classes, according to whatever categorisation makes sense (for example, put all your web-related beans into MyWebBeans.class, or split up the beans by feature, etc).
  3. At the top of the main config class, add a Spring @Import annotation that lists these subsidiary config classes, for example:
1
2
@Configuration
@Import({
    MyPersistenceBeans.class,
    MyServiceBeans.class,
    MyWebBeans.class
})
public class MainSpringConfig {}

For more advanced cases

Not every case is covered with this library, if you run into this please create a ticket. In the meantime it's very likely that it's possible to achieve whatever is missing by using the Gemini Blueprint API directly. Feel free to poke inside the decompiled OsgiServices class for examples.

Rate this page: