Updating to R7 annotations

26 views
Skip to first unread message

David Leangen

unread,
Feb 14, 2020, 2:08:27 AM2/14/20
to bndtool...@googlegroups.com

Hi,

Continuing on my journey to try to update to R7. I am now working on @Requirement and @Capability annotations. I am a little bit confused about how to make the transition.

Currently, I have annotations like this:

        @RequireCapability(
                ns = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
                filter = "(&"
                            + "(" + ImplementationNamespace.IMPLEMENTATION_NAMESPACE + "=" + CAP + ")"
                            + "(type=${type})"
                            + "${frange;" + VERSION + "}"
                         + ")"
        )
        @Retention(RetentionPolicy.CLASS)
        public static @interface Require
        {
            String type() default “my default";
        }

        @ProvideCapability(
                ns = ImplementationNamespace.IMPLEMENTATION_NAMESPACE
                name = CAP,
                version = VERSION )
        public static @interface Provide
        {
            String type();
        }

On the @Capability side, no problem at all:

        @Capability(
                namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE
                name = CAP,
                version = VERSION )
        public static @interface Provide
        {
            @Attribute("type")
            String value();
        }


I have figured out most of the puzzle on the @Requirement side:

        @Requirement(
                namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
                name = CAP,
                version = VERSION,
        )
        @Retention(RetentionPolicy.CLASS)
        public static @interface Require
        {
            String type() default Type.CONFINEMENT;
        }

However, the “type” part is still a mystery. The closest I got was including filter = “type=${type}”, but bnd complains that no such macro exists for ${type}.

How can I make all this work again with R7 bundle annotations? (The only thing I can think of right now is that I would need a separate annotation for each type I want to require...


Thanks for the help!!
=David


Tim Ward

unread,
Feb 14, 2020, 3:54:09 AM2/14/20
to bndtool...@googlegroups.com
Hi David,

The short answer is that you should be creating separate types for these things, assuming that they’re used often enough to be worth having a custom type.

Typically  the implementation namespace is used for whiteboards and other “indirect” relationships where there is no code or service dependency from the consumer on the provider. The number of providers in these setups is small, and so it is comparatively rare to find a custom @ProvideCapability annotation for them. The requiring side is more interesting, as there are typically a lot more users of the implementation than there are implementations.

In this situation an explicit, named annotation type is a better fit as it leaves fewer opportunities for usage errors.

I would therefore expect to see:

        @Capability(
                namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE
                name = CAP,
                version = VERSION )
        public static @interface Provide
        {
            @Attribute("type")
            String value();
        }

        // This type may be useful if there are lots of Confinement implementations
        @Provider(Type.CONFINEMENT)
        public static @interface ProvideConfinement
        {}

        // This is the type I would expect to see used most often
        @Requirement(
                namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
                name = CAP,
                version = VERSION
        )
        public static @interface RequireCapImplementation
        {}

        // If this type is needed a lot then it indicates a possible misuse of the Implementation namespace
        @Requirement(
                namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
                name = CAP,
                version = VERSION,
                filter = "(type=" + Type.CONFINEMENT")"
        )
        @Retention(RetentionPolicy.CLASS)
        public static @interface RequireCapConfinementImplementation
        {}

Obviously I only have the one code example to extrapolate from, and so I may be completely wrong here, but I would also like to add that the need to produce a “parameterised” requirement for different typed things within a single implementation namespace is an unusual thing to do, and makes me wonder whether the implementation namespace is being used appropriately. Are there situations where your “requirers” don’t care about the type of the “CAP” implementation, or is it always needed? If the implementation requirement really only makes sense when a type filter is applied then this indicates that the implementation namespace is probably not suitable for what you’re doing. 

The idea behind the implementation namespace is that you wish to avoid coupling to a specific implementation, but instead use whatever is available. You may sometimes need to be a little more selective, but in general your requirers should not care about any of the attributes (other than the name and version) on the capability that they require. If your requirers *do* need an implementation of a specific type then this means that either:

  • You should use the implementation namespace, but with different implementation names for each “type”. In really the various types are implementations of separate things, not different implementations of the same thing
  • You should create your own namespace, and continue using it as you are now, making it clear what the rules are (i.e. that the name attribute isn’t particularly important, and the type attribute is where the action is.

I hope this helps to clear things up. All the best,

Tim

--
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/C97AEE64-C725-42A2-83B6-1887D180CA49%40gmail.com.

Peter Kriens

unread,
Feb 14, 2020, 4:57:51 AM2/14/20
to via bndtools-users
The @Requirement and @Capability annotations that OSGi standardized in R7 are not as powerful as the corresponding bnd annotations that I developed for the original enRoute. The filter directive cannot refer to macros nor use any of the attributes in the meta annotated annotation.

However, as Tim states, the basic idea behind these annotations was to move things like `type` into the world of Java types. Might be inconvenient now and then but it does work and has certain advantages.

One more suggestion. The @Require* are more useful than the @Provide* annotations. I often find that it is easier to handle the Provide* directly in the manifest since the versions are generally more easily available.

Kind regards,

Peter Kriens


David Leangen

unread,
Feb 14, 2020, 6:08:38 AM2/14/20
to bndtool...@googlegroups.com

Thank you Tim and Peter for this information.

In this case, in my platform the “implementation” in question is an implementation of an authorization api (each using a different model / algorithm). There is no direct dependency, but it is required by each application. The default (if none is provided or if the default is explicitly asked for) is “restrictive”, meaning that anything that asks for authorization will be rejected.

“Confinement” is just another type of authentication, but “permissive” is another. There could be others in the future as well.

The assembler of the application needs to pull in one of the authentication implementations depending on the requirements of the app.


With that in mind, I will carefully read over your emails again tomorrow. With my explanation above if that triggers any further thoughts, of course I always hugely appreciate and value your input.


Best regards,
=David



Tim Ward

unread,
Feb 14, 2020, 7:41:19 AM2/14/20
to bndtool...@googlegroups.com
Ok,

So based on the information that you have provided the implementation namespace does sound like the right one to use, but your bundles which depend on the authorization api (which sounds not to be provided as a service for them to use) should have requirements without the “type” attribute in the selection filter. This allows you to use the “simple” Require annotation from my example. The application assembler is then responsible for setting up a repository and/or bndrun file which ensures that the authorization implementation that they want is deployed. 

If you do start putting the type attribute into the requirements then you will lose the ability to use a different authorization implementation with that bundle in the future.

The @Requirement and @Capability annotations that OSGi standardized in R7 are not as powerful as the corresponding bnd annotations that I developed for the original enRoute. The filter directive cannot refer to macros nor use any of the attributes in the meta annotated annotation.

Peter’s statement here is absolutely correct. The annotations were deliberately simplified for the specification because although the macro expressions are powerful they are also potentially confusing and error-prone. For example your macro can refer to information configured outside of the source file (e.g. in the bnd file) which may differ from build to build. This problem is compounded if/when class files from one project are packaged into another, for example the ${foo} macro may resolve to “bar” in the original build project, but “fizzbuzz” in the build project repackaging the code. This means that the content of the generated requirement will change unexpectedly depending on how and where the code is packaged, and it is non-trivial to work out what the manifest will look like based on the source code.

It was a bit of a wrench to give up the power of the macros, but I do believe it was done for the correct reasons, namely simplicity and readability.

All the best,

Tim

Peter Kriens

unread,
Feb 14, 2020, 8:14:29 AM2/14/20
to via bndtools-users
It was a bit of a wrench to give up the power of the macros, but I do believe it was done for the correct reasons, namely simplicity and readability.
The @Header supports all bnd macros, why is that argument then not the same for this annotation?

Kind regards,

Peter Kriens

Tim Ward

unread,
Feb 14, 2020, 9:11:52 AM2/14/20
to bndtool...@googlegroups.com
I think that the argument is the same, and it was my understanding that only the following (borrowed from the docs):

  • @class - gives the fully qualified name of the annotated class
  • @class-short - gives the simple name of the annotated class
  • @package - gives the package of the annotated class
  • @version - gives the package version of the annotated class

properties were supported. This restricted list is based on properties that are fully defined in source and therefore independent of the build (assuming the package version is declared using the @Version annotation). The fact that the full macro support is available for the Header annotation is (in my opinion) not a great idea, but I’m not looking to change anything or re-litigate the argument. My only goal was to attempt to explain to David why there are differences between the R7 annotations and the bnd annotations.

Tim

Raymond Auge

unread,
Feb 14, 2020, 9:22:38 AM2/14/20
to bndtool...@googlegroups.com
As specified the OSGi bundle annotations cannot be composed beyond one single meta annotation, that's the long and short of it. If you _want_ to compose them you need to go a step beyond and this was the rationale for Accessor Properties [1] which are now used in bnd's own feature, SPI annotations [2] and has already be used by outside developers (if you followed bnd's recent history closely you'll find both issues and discussions about that.)

- Ray




--
Raymond Augé (@rotty3000)
Senior Software Architect Liferay, Inc. (@Liferay)

Peter Kriens

unread,
Feb 14, 2020, 12:11:55 PM2/14/20
to via bndtools-users
We just are discussing this in a related issue and I find out the general bnd macros are available to the filter. 

I've just clarified a bit more about the use of macros in this area. See https://bnd.bndtools.org/chapters/230-manifest-annotations.html

Kind regards,

Peter Kriens

David Leangen

unread,
Feb 14, 2020, 3:35:53 PM2/14/20
to bndtool...@googlegroups.com

Hi Tim,

> The application assembler is then responsible for setting up a repository and/or bndrun file which ensures that the authorization implementation that they want is deployed.

Hmmmm. Perhaps I am missing something, but somehow I don’t feel comfortable with this.

Just to set up some context:

SystemAPI - api that is used by the system, but not directly as a service, and thus requires a cap/req means of being pulled into the system

SystemImplX - an implementation of the api in question, which provides the above-mentioned capability

So SystemImplA, SystemImplB, SystemImplC … are the different implementations of SystemAPI.

I hope this works for the discussion. :-)

What effectively you are saying:

* The application assembler is the one who sets up the assembly (I agree)
* This is done by curating the repository
—> If SystemImplA is the desired implementation, then make only it available from the repository
—> i.e. ensure that SystemImplB, SystemImplC… are **not** available
* All that is needed is a @RequireImplA annotation to set up the requirement

I agree that having a curated repository is very important. However, this seems IMO to be a misuse of the role of curation.

With deference to you, as you have much more experience than I do, it seems to me that the main role of curation is to ensure that none of the dependencies are pulling in other, unwanted dependencies (which can then make the dependency tree get out of control). To me, curation does not feel like the right place to control every single detail of the resolution process. That is what cap/reqs are for in the first place.

So instead of excluding SystemImplB, SystemImplC etc from the repository (they are not bad or dangerous, because they are not pulling in unwanted junk), rather the choice should be made at the same place where other similar choices are made, i.e. in the application descriptor:

@RequireSomething
@RequireSomethingElse
@RequireYetSomethingElse
@RequireSystemImplA
public class MyApp {}

It is perfectly fine and safe to keep all the implementations in the repository.


If one buys into this approach, then the next question is: which is better?

This?

* @RequireSystemA, @RequireSystemB, @RequireSystemC…

or this?

* @Requiresystem(“A”), @RequireSystem(“B”)…

But that is a different discussion. If you could first comment on the above issue of curation vs. capabilities, that would be helpful to me.

Thanks!
=David

David Leangen

unread,
Feb 15, 2020, 8:14:22 PM2/15/20
to bndtool...@googlegroups.com

Hi again,

I was rereading the thread and had an idea about what you may have meant.

The @Provide and @Require annotations (themselves annotated by @Capability and @Requirement) were actually part of an inner class. I could have rewritten them as:

@ProvideAuthorization
@RequireAuthorization

Then, the next question is “ok, but what *type* of authorization”? All of the “types” follow the same api, basically something simple like “boolean isAuthorized( String aToken )”.

Are you suggesting that I use a separate annotation to require a specific type of authorization? So like this?


@RequireAuthorization
@RequireConfinements
public class MyApp {}

// In this case, the ConfinementAuthorization service would match, as it is both an Authorization and a Confinement

Or are you suggesting that I have something like this?

@RequireConfinementBasedAuthorization
public class MyApp {}

// Also matches ConfinementAuthorization

If I want to change the authorization type, it would respectively become:

@RequireAuthorization
@RequireAbac
public class MyApp {}

// In this case, the ConfinementAuthorization service would match, as it is both an Authorization and a Confinement

Or are you suggesting that I have something like this?

@RequireAbacAuthorization
public class MyApp {}


All this as opposed to @RequireAuthorization(“confinement”) vs. @RequireAuthorization(“abac”).


Cheers,
=David

Peter Kriens

unread,
Feb 16, 2020, 9:33:53 AM2/16/20
to via bndtools-users
In the mean time I found out the macro processor IS available in the requirement processing ... 

So you have a choice now. The following would work

@Requirement( namespace="com.example.authorization", filter="(type=${#type})")
@interface RequireAuthorization { String type(); }

You could also use the "name" field:

@Requirement( namespace="com.example.authorization", name="${#type}") 
// filter:=(com.example.authorization=${#type})
@interface RequireAuthorization { String type(); }


I would prefer the approach of separate annotations when the set of authorization types is under your control, i.e. closed.

PK

-- 
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.

Tim Ward

unread,
Feb 17, 2020, 7:20:41 AM2/17/20
to bndtool...@googlegroups.com
Hi David,

The normal place to add @Requirement annotations (or meta annotations) is on a DS component (or similar) which has the requirement. In the authorization case it might be, for example:

@RequireAuthorization
@JaxrsResource
@Component(service = Foo.class)
public class Foo {
    @Path(“/foo”)
    public String checkStatus() {
        AuthorizationAPI.requirePermission(“check.status”);
        return “OK”;
    }
}

This says that the component requires an authorization implementation to back the static API (which I assume that you’re stuck with, a service would be better, and would then be included as a service dependency automatically.

From the example in your latest email it seems as though what you’re trying to do is to assemble an “empty” class which has requirements for various concrete implementations (rather than an implementation requiring the feature of another abstract implementation). This is a strange way to use the requirement annotations. Is this as an alternative to creating a bndrun file (i.e. you are using annotations to select things instead of run requirements)?

Normally I would expect an application assembler to use a bndrun + a repository to generate their deployment, not writing code. If they are writing code then it begins to get rather messy, as they have very different needs from other developers…

Best Regards,

Tim



David Leangen

unread,
Feb 17, 2020, 9:40:52 AM2/17/20
to bndtool...@googlegroups.com

Hi Tim,

From the example in your latest email it seems as though what you’re trying to do is to assemble an “empty” class

Yes, that is true. I am using the idea of an “application descriptor”. I think this idea may have come from Peter, but I’m not able to find a reference right now. Are you suggesting that you do not like the idea of using requirements to define an application? You (as an assembler) assemble the entire application in a bndrun file?

Do you have any examples you can show me? I would be interested to see how you do this.

The point of what I am trying to do is to use an “Application” class to describe the application, at least from the abstract level. The application descriptor is nothing more than a set of requirements: It pulls in the implementations it needs by way of requirements. Using typed annotations makes working with requirements quite a lot easier, hence having the empty class to describe the application. I would much rather have the resolver assemble the bndrun file than do it by hand, if that is what you mean.

which has requirements for various concrete implementations (rather than an implementation requiring the feature of another abstract implementation).

No, I don’t believe this is the case. It’s just a different level of abstraction. I could have a dependency on “money”, or I could have a dependency instead on USD. Although USD is more specific than “money” it is still abstract (in this analogy: I did not say if it was cash, what kind of denominations, or whatever… it still requires an implementation, but I don’t care so long as it respects the contract).

As I mentioned, I have different types of authentication, based on known types (i.e. part of the contract). Although more specific than just “authentication”, it is still in the abstract, and needs to be decided system-wide for each application. So it would not be appropriate at the component level (two components could define a different type, and then I’d essentially need a new component for each type of authorization, which of course would not make sense).

This is a strange way to use the requirement annotations. Is this as an alternative to creating a bndrun file (i.e. you are using annotations to select things instead of run requirements)?

If we were talking about an actual implementation, rather than an api, I would agree. However, I think I was not able to express myself well. Sorry I lack more precise vocabulary to express this.

Normally I would expect an application assembler to use a bndrun + a repository to generate their deployment, not writing code. If they are writing code then it begins to get rather messy, as they have very different needs from other developers…

Again, I think this makes sense if we were talking about implementations.


Cheers,
=David



Tim Ward

unread,
Feb 17, 2020, 11:26:40 AM2/17/20
to bndtool...@googlegroups.com
Hi David,

I primarily use Maven for my OSGi development, but the model is still similar, namely I have a build project which assembles my application from a bndrun file. The “editable” parts of the bndrun include things like system properties and the run requirements, the actual list of bundles is always resolved automatically and not maintained by hand.

There are plenty of examples of this in the OSGi enRoute project, and if you’re using Bndtools then the bndrun editor will allow you to drag and drop requirements (so there’s no need to type a long filter).

Tim

David Leangen

unread,
Feb 18, 2020, 3:02:18 AM2/18/20
to bndtool...@googlegroups.com

Hi Tim,

Thanks!

I took a look at enRoute as you suggested. Since I don’t see the difference between annotating an empty class and annotating a package like how I see it being done, for instance, in the rest-app project, I would say there isn’t really much difference between what I am doing and what you are describing. The point of the “build project” or “application project” or “application descriptor” or whatever one wants to call it seems to be exactly the same, and the actual mechanism is essentially the same.

All this does indeed help deepen my understanding, so much appreciated. Thanks!


Cheers,
=David



Tim Ward

unread,
Feb 19, 2020, 6:27:35 AM2/19/20
to bndtool...@googlegroups.com
Hi David,

I took a look at enRoute as you suggested. Since I don’t see the difference between annotating an empty class and annotating a package like how I see it being done, for instance, in the rest-app project, I would say there isn’t really much difference between what I am doing and what you are describing.

I think that this is a misunderstanding of what the source/resources in the rest-app project are doing. The rest-app project packages a bundle containing the application configuration (in JSA using the OSGi Configurator specification. The package annotated with @RequireConfigurator is therefore present to make sure that the relevant configurator extender requirement is added to the configuration bundle.

The actual resolve/assembly of the application is entirely controlled by the requirements in the bndrun file.

Tim

Reply all
Reply to author
Forward
0 new messages