This page describes how apply conditions to Spring beans defined in your P2 app's Spring Java configuration.
You can apply Condition
s to your @Bean
methods or even entire @Configuration
classes in the
usual Spring way.
You simply annotate the class or method with @Conditional
and nominate the Condition
class (or classes) to apply,
for example
1 2@Conditional({SomeCondition.class, SomeOtherCondition.class}) @Bean public FooService fooService() { // instantiate and return the FooService implementation }
Our helper library (see Dependencies) includes the following conditions to get you started:
AbstractSystemPropertyCondition
: subclass this and provide the name and value of a system property. The condition
will be true
if that property is set to that value.DevModeOnly
: returns true
if the atlassian.dev.mode
system property is set to true
.BambooOnly
: returns true
if the host product is Bamboo.BitbucketOnly
: returns true
if the host product is Bitbucket Server.ConfluenceOnly
: returns true
if the host product is Confluence Server.CrowdOnly
: returns true
if the host product is Crowd.FecruOnly
: returns true
if the host product is Fisheye/Crucible.JiraOnly
: returns true
if the host product is Jira Server (or any variant of it).RefappOnly
: returns true
if the host product is the Atlassian Reference Application.@ConditionalOnClass
annotationSometimes the Condition
s listed above are not sufficient to decide whether the class will be present, for instance because it is present in some versions of the product but not others, or is provided by a plugin which might not be installed and enabled.
In these cases you can annotate a config class with @ConditionalOnClass({ClassThatMightNotExist.class, ...})
. This will prevent that config class from being processed if any of the classes mentioned cannot be loaded. You can also annotate an @Bean method, but if you annotate a method instead of the entire class you will need to ensure that the class which may not be able to be loaded is not used in the signature of the annotated method, except as a type parameter.
This annotation has the same name and semantics as the equivalent Spring Boot annotation, but it is a separate implementation.
There are a couple of things to be aware of when making bean definitions conditional.
If a conditional bean method's signature and/or return type refer to a class that is not always on the classpath, i.e. is not available in all intended target environments (for example because it's product or version specific), then you need to do two things:
Declare the OSGi import for that class as optional
. Otherwise, your plugin will throw an OSGi missing package error
(and thereby fail to load) in any environment in which that class does not exist. For example, if you declare a bean
method that accepts or returns Jira's IssueService
, and your plugin needs to load into products other than Jira, then
you should declare that type's package as optional in the AMPS OSGi instructions, as follows:
1 2<Import-Package> com.atlassian.jira.bc.issue;resolution:=optional, * </Import-Package>
When Spring processes your Java config classes, it will introspect them using reflection, to detect the bean methods. If
any of the types mentioned in these methods' signatures or return types are not on the classpath, Spring will throw a
ClassNotFoundException
, and your ApplicationContext
will not load. The one exception to this is generic types, which
are erased by the Java compiler before Spring performs this introspection. There are therefore two ways to prevent
ClassNotFoundException
s being thrown for types that may legitimately not exist:
@Conditional
(or @ConditionalOnClass
), and apply a condition that will prevent Spring from processing it in
situations where the relevant class is known to be absent. For example, if a bean method returns a product-specific type
like Jira's IssueService
, annotate the config class with the JiraOnly
condition mentioned above. Spring will
evaluate this class-level condition, and if the running product is not Jira, skip the introspection step altogether,
avoiding the ClassNotFoundException
. Here's what that might look like:1 2// Spring will skip this entire class if the running product is not Jira @Configuration @Conditional(JiraOnly.class) public class MyJiraOnlyBeans { @Bean public IssueService issueService() { return OsgiServices.importOsgiService(IssueService.class); } }
FactoryBean<T>
, where
T
is the type that may or may not exist. Because the compiler will erase the reference to T
, the absence of that
class will not cause Spring to throw a ClassNotFoundException
. Here's what this approach might look like:1 2// Spring will unconditionally process this config class... @Configuration public class MyBeans { // ... but only create this bean if the running product is Jira @Bean @Conditional(JiraOnly.class) public FactoryBean<IssueService> issueServiceFactoryBean() { return OsgiServices.factoryBeanForOsgiService(IssueService.class); } }
Rate this page: