• Intro Headers Instructions Macros Commands
  • Fork me on GitHub
    • Introduction
    • How to install bnd
    • Guided Tour
    • Guided Tour Workspace & Projects
    • Concepts
    • Best practices
    • Build
    • Project Setup
    • Generating JARs
    • Versioning
    • Baselining
    • Service Components
    • Metatype
    • Contracts
    • Bundle Annotations
    • Accessor Properties
    • SPI Annotations
    • Resolving Dependencies
    • Launching
    • Startlevels
    • Testing
    • Testing with Launchpad
    • Packaging Applications
    • JPMS Libraries
    • Wrapping Libraries to OSGi Bundles
    • Generating Documentation
    • Commands
    • For Developers
    • Tips for Windows users
    • Tools bound to bnd
    • Headers
    • Instruction Reference
    • Instruction Index
    • Macro Reference
    • Macro Index
    • Plugins
    • External Plugins
    • Settings
    • Errors
    • Warnings
    • Frequently Asked Questions
  • Accessor Properties

    When working with custom bundle annotations one may find that @Attribute and @Directive have limitations in scenarios where multiple bundle annotations are added to a single custom bundle annotation because there are no discriminators expressing which bundle annotation to associate the attribute or directive; and it’s likely that associating these with all bundle annotations produces unexpected or even incorrect results. Bnd provides a solution which takes advantage of its macro support during annotation processing.

    Accessor Properties

    When an annotation is meta-annotated with a bundle annotation the values and defaults of the host are loaded as properties, prefixed with #, into the macro processing scope. The effect is that these values are accessible using a macro pattern (like ${}).

    @Capability(
        name = "${#name}", // <-- accesses the hosts 'name()' value
        namespace = "osgi.cdi.extension",
        version = "${#version}"  // <-- accesses the hosts 'version()' value
    )
    @Requirement(
        name = "osgi.cdi",
        namespace = "osgi.implementation",
        version = "1.0.0")
    @interface CDIExtension {
        String name();
        String version();
    }
    

    Annotation methods may return several different types and they are converted to strings according to the following table:

    Return type Conversion
    boolean, byte, char, double, float, int, long, short String.valueOf()
    java.lang.Class Class.getName()
    java.lang.Enum Enum.name()
    java.lang.String as is (commas should be escaped with \)
    array type whose component type is one of the preceding types joined by comma (,)
    type satisfying Class.isAnnotation() omitted from available accessor properties
    array type whose component type satisfies Class.isAnnotation() omitted from available accessor properties

    It should be noted that if the resulting strings contain macro characters these will be interpreted by bnd’s macro engine. This may produce unexpected results due to potentially nested macros. In this scenario steps must be taken to either escape these characters or that nesting is done to allow bnd to successfully process the result.

    The annotated type as default

    The return type Class<?> is particularly useful when it comes to making your code friendly to refactoring.

    @Capability(
        filter = "objectClass:List<String>='${#value}'",
        namespace = "osgi.service",
    )
    @interface MyService {
        Class<?>[] value();
    }
    
    // applied as
    
    @MyService(Bar.class)
    class Bar { ... }
    

    What if you want the default to be the annotated class itself? Bnd made this possible by using a type which was unlikely to be a real argument. The type selected was java.lang.annotation.Targetbecause it is guaranteed to be accessible, is unlikely to be a real argument, will not require an additional dependency, and it’s nomenclature proved the most expressive for the use case.

    @Capability(
        filter = "objectClass:List<String>='${#value}'",
        namespace = "osgi.service",
    )
    @interface MyService {
        Class<?>[] value() default Target.class;
    }
    
    // applied as
    
    @MyService
    class Bar { ... }
    

    Instances of java.lang.annotation.Target.class found in the return value of Class<?> or Class<?>[] methods will be replaced by the annotated type prior to string conversion.

    Cardinality

    In order to control the cardinality of requirements generated by custom bundle annotations a convenience enum aQute.bnd.annotation.Cardinality is available as a method return type. The default value should be Cardinality.DEFAULT ensuring that if not set no directive will be emitted.

    @Requirement(
        attribute = {
            aQute.bnd.annotation.Constants.CARDINALITY_MACRO
        },
        namespace = "osgi.service",
    )
    @interface RequireMyService {
        Cardinality cardinality() default Cardinality.DEFAULT;
    }
    
    // applied as
    
    @RequireMyService(cardinality = Cardinality.MULTIPLE)
    class Bar { ... }
    

    Note: It is recommended to use the macro constant aQute.bnd.annotation.Constants.CARDINALITY_MACRO crafted specifically for handling the cardinality directive. It should also be noted that during macro processing the enum name() will be converted to lower case.

    Resolution

    In order to control the resolution of requirements generated by custom bundle annotations a convenience enum aQute.bnd.annotation.Resolution is available as method return type. The default value should be Resolution.DEFAULT ensuring that if not set no directive will be emitted.

    @Requirement(
        attribute = {
            aQute.bnd.annotation.Constants.RESOLUTION_MACRO
        },
        namespace = "osgi.service",
    )
    @interface RequireMyService {
        Resolution resolution() default Resolution.DEFAULT;
    }
    
    // applied as
    
    @RequireMyService(resolution = Resolution.OPTIONAL)
    class Bar { ... }
    

    Note: It is recommended to use the macro constant aQute.bnd.annotation.Constants.RESOLUTION_MACRO crafted specifically for handling the resolution directive. It should also be noted that during macro processing the enum name() will be converted to lower case.

    Effective

    In order to control the effective time of a requirement generated by custom bundle annotations a method called effective with a return type of String can be used in conjunction with the macro constant aQute.bnd.annotation.Constants.EFFECTIVE_MACRO. The default value should be the empty string ("") ensuring that if not set no directive will be emitted.

    @Requirement(
        attribute = {
            aQute.bnd.annotation.Constants.EFFECTIVE_MACRO
        },
        namespace = "osgi.service",
    )
    @interface RequireMyService {
        String effective() default "";
    }
    
    // applied as
    
    @RequireMyService(effective = "active")
    class Bar { ... }
    

    Uses

    In order to control the uses constraints of a requirement generated by custom bundle annotations a method called uses with a return type of Class<?>[] can be used in conjunction with the macro constant aQute.bnd.annotation.Constants.USES_MACRO. The default value should be an empty array ({}) ensuring that if not set the directive will not be emitted.

    @Requirement(
        attribute = {
            aQute.bnd.annotation.Constants.USES_MACRO
        },
        namespace = "osgi.service",
    )
    @interface RequireMyService {
        Class<?>[] uses() default {};
    }
    
    // applied as
    
    @RequireMyService(uses = {com.acme.Foo.class, org.bar.Bar.class})
    class Bar { ... }
    

    ####

    • GitHub