@ProviderType

263 views
Skip to the first unread message

David Leangen

unread,
20 Jun 2017, 20:13:2620/06/2017
to bndtool...@googlegroups.com

Another issue I am struggling with: @ProviderType.

I am attempting to use a DDD-ish approach to development. This means that I have mainly Entities, ValueObjects, and Services. I have API/impl separation: an API interface for most (but not all) domain objects in an API package, and an implementation of these objects in a provider package.

Now, most of these interfaces (but not all) have exactly one implementation class. (Even if there is generally only a single provider per API, I still find it helpful to have a clearly separated API, and implement against that API.) So in a sense, the interfaces are all “providers” because they “provide” the contract for the implementation. However, they are also “consumed” by other objects. Each of the interfaces is necessarily provided, and most probably consumed. When they are consumed, it is likely by more than one other package.

So, are these @ProviderType or @ConsumerType? Looks like I have to choose, but I’m not sure how.


I have read the white paper [1], and the RFC [2], but the examples used aren’t helping me resolve these questions.

Any advice? (Or is this too abstract?)


Cheers,
=David




BJ Hargrave

unread,
20 Jun 2017, 20:27:0120/06/2017
to bndtool...@googlegroups.com
Who is supposed to implement the interfaces? Consumers of the API (such as Listeners) or providers of the API (such as services). In your case it sounds like the interfaces are provider type and you also have the implementations available to users of the API.

--
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.
For more options, visit https://groups.google.com/d/optout.
--
BJ

David Leangen

unread,
20 Jun 2017, 20:49:0020/06/2017
to bndtool...@googlegroups.com

Thanks, BJ,

Ok, I now see what you mean by:

Who is supposed to implement the interfaces?


I took another look at the RFC and noticed this (formatting is mine):

Part of the Semantic Versioning mechanics concern whether an API is implemented by providers of the technology or by consumers of the technology. This distinction governs how the version of this API is updated given a change to the Interface.

I did not originally catch that part. Thanks for helping me understand!!



Cheers,
=David

David Leangen

unread,
20 Jun 2017, 22:34:0420/06/2017
to bndtool...@googlegroups.com

By the way, if the choice is made based on which class implements the interface, then posts like this one by Marcel Offermans are misleading:


He seems to imply the “consume” means to “use” or “reference” the interface.

Let’s take EventAdmin as an example. If you want to use that API to send an event, you consume the EventAdmin service and invoke a method on it. That is actually a pretty common use case. If we add a new method to EventAdmin, this would be a minor change, backward compatible with existing users as this is an interface that is “provided”, so adding a method does not change anything for them. However, when you want to listen to events, you need to implement EventHandler and register it as a service in the service registry. When we would add a method to EventHandler, it would suddenly not be a backward compatible change as in Java (prior to Java 8 and its default implementations) you must implement all methods of an interface. In other words, existing users now “consume” the interface, and adding a method breaks their implementations. So, when designing an API, we must somehow express that an interface is either a “provider” or “consumer” type. For this, Bndtools added two annotations: @ProviderType and @ConsumerType. When you annotate your interfaces like this, semantic versioning will take this into account and will correctly bump either the minor or major version when you add a method to a type.


Well, under this definition of “consume", the EventAdmin is both implemented and consumed, so it does not help with the choice between @ProviderType or @ConsumerType. If the choice is only made based on how the interface is implemented (as suggested by BJ and as written in the RFC), then a reasonable choice can be made.

—> So the key is how the interface is implemented (a provider-type implementation, or a consumer-type implementation), not how it is used by other classes.


Is my understanding correct?


Cheers,
=David

Raymond Auge

unread,
20 Jun 2017, 23:22:3320/06/2017
to bndtool...@googlegroups.com
Let's take the classic example I always use:

You've likely implemented a Servlet at some point. Is there any way you could have used that API without implementing that type? Not likely! In fact, pretty much anyone using the servlet API has to implement the Servlet type.

Since everyone has to implement that type it's clearly meant to be implemented by consumers of the API. Therefore, it's a consumer type.

On the other hand, how many times in those servlet programs had you "implemented" (Http)ServletRequest? I would guess close to never. Why? This type is both instantiated and handed to your servlet code form somewhere else; the servlet container. The servlet container is a provider of that type. The type is always implemented by the servlet container... the provider. Therefore it's a provider type.

In summary:

A @ConsumerType is one where in order to make use of the type the average program has to implement/instantiate an instance.

A @ProviderType is one where in order to make use of the type the average program is "passed" an instance from elsewhere... a provider.

HTH
- Ray



To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
--
BJ

--
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-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.


--
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-users+unsubscribe@googlegroups.com.

Peter Kriens

unread,
21 Jun 2017, 02:59:3521/06/2017
to bndtool...@googlegroups.com
OSGi semantic versioning has NOTHING to do with who implements what interface. It is all about the role a bundle has towards an API, not technical details. This is the reason we talk about consuming an API and providing an API and not ‘implement’ an API and ‘use’ an API. Again, it is utterly unrelated to implementing/extending/using a Java type! Providers implement and use types in the API and so do consumers. 

An API is a contract that defines a number of _roles_. Some of these roles define an obligation on the provider, and some of the roles are an obligation on the consumer. The best example is Event Admin. The EventAdmin interface is implemented by the provider because virtually any change in the contract will require a new implementation for the Event Admin interface. However, the EventHandler interface is implemented by a consumer. We’ve been able to revision the API several times without ever breaking an implementation of an Event Handler. When we added a new feature we could get away with a minor version increment because we only broke the providers. If we ever change the EventAdmin interface we will need a major version change because it is a consumer of the API.

In the following table you see that bot the consumer and provider use and implement interfaces in this API:

EventAdmin EventHandler
consumer  uses  implements
provider implements uses

Semantic version is about promising how you will signal breaking backwards compatibility. Your future releases will break people that consume your product and, in the case of APIs, your or people that provide an alternative. 

Therefore, to decide if a class is a provider or a consumer of an API think of future scenarios. The consumers are the people you really do not want to break lightly (usually your customers) and the providers are the implementations that need to be broken for virtually every change in the API (usually you).

Again, it is the role in the API, not about the interfaces itself.

In your example the interfaces sound like provider types. If you release a new product you likely make these interfaces backward compatible for their users but they will break your implementations (the providers). However, you likely have listener services in there that you do not want to break, those would be the consumer types.

Hope this helps, kind regards,

Peter Kriens


--
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.

David Leangen

unread,
21 Jun 2017, 04:01:5321/06/2017
to bndtool...@googlegroups.com

Thanks, Peter. There are a few nuances that I am still not quite grasping, but I’ll stick to my main issue only here.

On Jun 21, 2017, at 3:59 PM, Peter Kriens <pkr...@gmail.com> wrote:
Therefore, to decide if a class is a provider or a consumer of an API think of future scenarios.

If a class is a provider or consumer of an API, to me that is not too difficult to understand. I also understand why the consumer and the provider would be affected differently by different types of changes to the API.

So, maybe I am misunderstanding somewhere else. I’ll try to use a contrived example.

Say I have an package: com.example.api (1.0.0), which has two interfaces:
  interface Foo
  interface Bar

Now say I have a provider package (com.example.provider) and a consumer package (com.example.consumer).


My understanding of the @ProviderType and @ConsumerType annotations is that they would appear on Foo and (or???) Bar, so:
  @{Provider|Consumer}Type interface Foo
  @{Provider|Consumer}Type interface Bar

I can identify who the consumers and providers of the API are. That’s easy enough: com.example.provider is the provider and com.example.consumer is the consumer. But since the API has both consumers and providers, how the heck do I determine if Foo and (or???) Bar should be annotated as @ConsumerType or @ProviderType?

Is it related to this?
Semantic version is about promising how you will signal breaking backwards compatibility. Your future releases will break people that consume your product and, in the case of APIs, your or people that provide an alternative. 


Or are these annotations supposed to be used in a completely different way?


Cheers,
=David

Peter Kriens

unread,
21 Jun 2017, 04:40:1721/06/2017
to bndtool...@googlegroups.com
On 21 Jun 2017, at 10:01, David Leangen <david....@gmail.com> wrote:
Thanks, Peter. There are a few nuances that I am still not quite grasping, but I’ll stick to my main issue only here.
You’re not the only one, just honest about :-)

On Jun 21, 2017, at 3:59 PM, Peter Kriens <pkr...@gmail.com> wrote:
Therefore, to decide if a class is a provider or a consumer of an API think of future scenarios.
If a class is a provider or consumer of an API, to me that is not too difficult to understand. I also understand why the consumer and the provider would be affected differently by different types of changes to the API.

So, maybe I am misunderstanding somewhere else. I’ll try to use a contrived example.

Say I have an package: com.example.api (1.0.0), which has two interfaces:
  interface Foo
  interface Bar

Now say I have a provider package (com.example.provider) and a consumer package (com.example.consumer).


My understanding of the @ProviderType and @ConsumerType annotations is that they would appear on Foo and (or???) Bar, so:
  @{Provider|Consumer}Type interface Foo
  @{Provider|Consumer}Type interface Bar

I can identify who the consumers and providers of the API are. That’s easy enough: com.example.provider is the provider and com.example.consumer is the consumer. But since the API has both consumers and providers, how the heck do I determine if Foo and (or???) Bar should be annotated as @ConsumerType or @ProviderType?

Well, that is simple. Any interface implemented by the com.example.consumer is ‘precious’. You do not want to break consumers when you have a new release. So if Foo and/or Bar are implemented by the consumer they are @ConsumerType. I.e. you promise the consumers that they you will not lightly make changes that affect them. That is, you go out of your way to not make binary class changes that affect them, I.e. not extend interfaces they implement and not remove methods from interfaces/classes they use.

If an interface is solely implemented by a provider, or a class extended, then you can add a method to it because it only breaks the provider. Since a provider needs to change for virtually each API change anyway, this is not a big deal. 

1.x to 2.x major change break providers AND consumers
1.1 to 1.2 minor change break providers
1.1.1 to 1.1.2 micro change break nobody

So you have the following table:

Provider Consumer
Foo  impl impl @ConsumerType Foo (rare)
Foo  impl use @ProviderType Foo (eg EventAdmin)
Foo  use impl @ConsumerType Foo (eg EventHandler)
Foo  use use @ProviderType Foo (eg Event class)

Is it related to this?
Semantic version is about promising how you will signal breaking backwards compatibility. Your future releases will break people that consume your product and, in the case of APIs, your or people that provide an alternative. 
Yes. The trick is to see the API as a first class citizen in the versioning. Since an API has 2 sides, there are different rules for the consumers of that API and the providers. For example, if you sell a house then you are the provider. The buyer (consumer) does not mind if you add an extra room in the house but they tend to get upset when a room is missing on delivery. 

bnd can calculate the binary compatibility so it knows exactly when a bundle gets broken in baselining. However, it then needs to know the consequences of that incompatibility. If it finds that a provider got broken then it knows it is a minor change, otherwise it is a major change. The annotation tells bnd that the interface breaks a provider or a consumer.

Since you have to be safe in these things, the ConsumerType is actually not necessary since it is the default. I.e. the @ProviderType is the only one that tells bnd that if a bundle implements that interface with an @ProviderType annotation it is ok to treat it as a minor change. Otherwise it is a major change.

Or are these annotations supposed to be used in a completely different way?
I hope it is clearer now. In my experience so far it is usually that people hang on too long on thinking consume = implement interface. Provider and Consumer are really about what role you have in the API contract. However, in the end it is just a promise of how you change your API. It is perfectly ok to mark all interfaces as Consumer Types. Just means that almost all changes will be a major change. 

Kind regards,

Peter Kriens

Milen Dyankov

unread,
21 Jun 2017, 05:16:4221/06/2017
to bndtool...@googlegroups.com
Out of curiosity, is it possible to tell bnd to use different annotation to determine the type? For example can I somehow specify that anything annotated with @MyCustomSpecialService is a @ProviderType

Best,
Milen 






Cheers,
=David



To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
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-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
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-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
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-users+unsubscribe@googlegroups.com.

Peter Kriens

unread,
21 Jun 2017, 05:22:2621/06/2017
to bndtool...@googlegroups.com
Nope. Before I figured out how useful meta annotations could be I thought they were insane :-) I still think you have to be careful because it significantly reduces the readability.

In this case I wonder if it is worth it. The meta annotations in manifest headers work great because you can encode the configuration information in Java types. However, in this case the information is a boolean only. Do you have any reason in mind or just convenience of lifting with an another annotation?

Kind regards,

Peter Kriens

To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.

Timothy Ward

unread,
21 Jun 2017, 05:35:1621/06/2017
to bndtool...@googlegroups.com
And just to complete Ray’s Servlet analogy…

Even though people *sometimes* want to implement HttpServletRequest so that they can wrap/decorate it in some way it is *still* @ProviderType because the Servlet API specifically tells Servlet Authors not to implement that type. 

If you want to provide wrapping/decoration of incoming HTTP requests you are supposed to use the javax.servlet.http.HttpServletRequestWrapper type. This wrapper type is (by definition) @ConsumerType because it is explicitly intended to be subclassed by developers.

Effectively the @ProviderType and @ConsumerType are formal definitions of what is normally contained in well written JavaDoc, and they are also available to tools (e.g. bnd baselining) for introspection/analysis.

I hope this helps to further your understanding.

Regards,

Tim


To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.

Milen Dyankov

unread,
21 Jun 2017, 05:43:4421/06/2017
to bndtool...@googlegroups.com
Nothing in particular. And I didn't really mean meta annotations. I was just wondering as one can (and some do) use baselining for non-OSGi projects where there is a whole bunch of frameworks and annotations used. So I was curious if instead of asking people to add yet another annotation one can simply configure bnd to "guess" based on existing annotations. I guess it all boils down to answering "where would one put @ProviderType annotation?" and in some cases that may be handled by some logic. I don't really understand why would one ever use @ConsumerType as it is the default. 

Just thinking out loud. Not even sure it this makes sense. 

Peter Kriens

unread,
21 Jun 2017, 05:45:0421/06/2017
to bndtool...@googlegroups.com
:-)
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.

David Leangen

unread,
21 Jun 2017, 14:16:0121/06/2017
to bndtool...@googlegroups.com

Yes, I think I “get it” now. I have resolved pretty much all the issues I was having in my head.

This community seems fairly small, but its helpfulness makes it one of the best communities I have participated in. Thanks so much to all!!


Cheers,
=David

David Leangen

unread,
15 Jul 2017, 04:06:3015/07/2017
to bndtool...@googlegroups.com
> On Jun 21, 2017, at 5:40 PM, Peter Kriens <pkr...@gmail.com> wrote:
>> Thanks, Peter. There are a few nuances that I am still not quite grasping, but I’ll stick to my main issue only here.
> You’re not the only one, just honest about :-)

Thanks to everybody’s help, I understand this a lot better.

One more question.

In my API, I have many interfaces. As I mentioned in my original post to this thread:

> I am attempting to use a DDD-ish approach to development. This means that I have mainly Entities, ValueObjects, and Services. I have API/impl separation: an API interface for most (but not all) domain objects in an API package, and an implementation of these objects in a provider package.

My question is: to which interfaces should the @ProviderType annotation be applied?

How does bnd pick up this information during baselining? For instance, since @ConsumerType is more restrictive, would that “win” if there is only one single @ConsumerType in a bundle?

In other words, if I want my API package to be of @ProviderType, since @ConsumerType is the default, would I have to annotate every single interface in that package as @ProviderType?



Cheers,
=David


Peter Kriens

unread,
15 Jul 2017, 07:55:1815/07/2017
to bndtool...@googlegroups.com

> On 15 Jul 2017, at 10:06, David Leangen <david....@gmail.com> wrote:
>
>> On Jun 21, 2017, at 5:40 PM, Peter Kriens <pkr...@gmail.com> wrote:
>>> Thanks, Peter. There are a few nuances that I am still not quite grasping, but I’ll stick to my main issue only here.
>> You’re not the only one, just honest about :-)
> Thanks to everybody’s help, I understand this a lot better.
> One more question.
> In my API, I have many interfaces. As I mentioned in my original post to this thread:
>
>> I am attempting to use a DDD-ish approach to development. This means that I have mainly Entities, ValueObjects, and Services. I have API/impl separation: an API interface for most (but not all) domain objects in an API package, and an implementation of these objects in a provider package.
Not sure if it makes sense to talk about a provider package for implementations? Implementations should never be exported/imported?

> My question is: to which interfaces should the @ProviderType annotation be applied?
Well, I guess we already discussed this? Any type that you will not keep backward compatible between minor releases.

> How does bnd pick up this information during baselining? For instance, since @ConsumerType is more restrictive, would that “win” if there is only one single @ConsumerType in a bundle?
> In other words, if I want my API package to be of @ProviderType, since @ConsumerType is the default, would I have to annotate every single interface in that package as @ProviderType?
bnd assumes a minor (provider) range when there is a class in the bundle that implements, directly or indirectly, an interface marked as Provider.

So the answer is yes.

Kind regards,

Peter Kriens

>
>
>
> Cheers,
> =David
>
>

David Leangen

unread,
15 Jul 2017, 07:59:2815/07/2017
to bndtool...@googlegroups.com

Thanks, Peter!

Cheers,
=David
Reply all
Reply to author
Forward
0 new messages