felix-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Richard S. Hall" <he...@ungoverned.org>
Subject Re: Object versions in services
Date Fri, 14 Jun 2013 13:56:13 GMT
On 6/14/13 06:43 , Mark Richards wrote:
>   The WireAdmin Producer class has a polled method that is similar that just
> returns an Object. I've used Generics as a good programming practice, but
> I'm aware it doesn't solve this problem.
> There seems to be a use case for this nature of functionality. I've no idea
> how frameworks like Camel are solving this problem in OSGi... do they just
> hope for the best and push Objects are around and assume Producers and
> Consumers are using the same versions?
>
> I understand that the framework wouldn't know what T is, so I'd quite like
> to tell the framework what T is.
> Ie:
> Properties props ...
> props.put("providerClassName",Model.class.getName());
> props.put("providerClassVersion",*
> context.getImportVersion(Model.class.getPackage().getName()*); //this
> doesn't exist
> context.register(Provider.class,this,props);
>
> However, there doesn't seem to be an easy way to do this,  unless I've
> missed something.

Yes and no.

At the module level, if you know that the service API will always be 
exposing the model API, then your service API bundle should import the 
model package and list it as a "uses" constraint on the export of the 
service package. This will then ensure that any consumers of the service 
package will also be compatible with the service's model package.

If you want to have many providers of the service with different model 
versions being exposed, then different providers will likely have to 
export the service package and export it where they import a the 
specific version of the model they want so their exported service 
package is bound to their desired model version via the "uses" constraint.

It is not possible at the module level to maintain consistency with a 
single shared service package where each provider exposes different 
versions of downcastable classes from the service API. If you really 
need this, then you have to do it yourself at the service level.

For example, in your service registration you could add properties to 
indicate the bundle ID from which the provider gets its model package, 
then consumers would have to use a service filter that also matched the 
bundle ID from which they get the model package. This would work, but 
not very elegant.

In short, this whole situation results because of a lie being told in 
your API. The API gives the illusion that the service is indendent of 
the downcastable type, which makes you believe that you should have a 
single class space for all service providers, but in reality you don't 
have a single class space. You have one class space for each service + 
model combination.

No client in Java (with or without OSGi) can expect to downcast reliably 
in the scenario you are creating.

-> richard

>
> Many Thanks
> Mark
>
>
> On 14 June 2013 04:36, Richard S. Hall <heavy@ungoverned.org> wrote:
>
>> On 6/13/13 20:55 , Mark Richards wrote:
>>
>>> I thought I'd implement an example of this and I get ClassCastExceptions
>>> in
>>> the Consumer.
>>> Please feel free to checkout:
>>> https://bitbucket.org/**markalanrichards/discussion-**points/overview<https://bitbucket.org/markalanrichards/discussion-points/overview>
>>> It's not that big (each bundle has only one Java class in it).
>>>
>>> When these start (in order) everything is fine: service-api, model10,
>>> model15, model20, model-provider10, model-provider15, model-provider20.
>>> However, when I start modelConsumer10, model 1.0 and model 1.5 are fine
>>> for
>>> the consumer, but model 2.0 hits the ClassCastException.
>>>
>>> What did I miss?
>>>
>> You are basically hiding package dependencies behind generic types. There
>> is no easy way for the framework to do a consistency check for "T
>> provide()" in a Provider<T>. It can't do it at resolve time because there
>> is no "uses" constraint on T since it doesn't know what it is; it
>> technically might be able to figure it out at run time when doing service
>> lookups, but that would be tricky and costly and still not complete since
>> incompatible stuff could still be passed back in Objects or supertypes and
>> accessed via downcasting.
>>
>> The only guarantees OSGi gives you are with respect to packages and their
>> associated "uses" constraints, your example expects it to analyze all
>> reachable types from a given service interface at run time. It doesn't do
>> that.
>>
>> You would have to create specific concrete versions of your generic
>> interface for each model type so that you could establish a "uses"
>> constraint between the service interface and the concrete model it provides.
>>
>> -> richard
>>
>>
>>
>>> Many Thanks
>>> Mark
>>>
>>> PS: If you don't wish to checkout/run this, but are interested in what
>>> happens, the output when I start model-consumer10 is:
>>> finding refs
>>> objectClass: [Ljava.lang.String;@df54977
>>> provides: com.example.markoffline.model.**Model
>>> service.id: 226
>>> TO STRING: Date: 1371160741853
>>> Description: null
>>> THE LONG: 1371160741853
>>> objectClass: [Ljava.lang.String;@11df24ba
>>> provides: com.example.markoffline.model.**Model
>>> service.id: 227
>>> TO STRING: Date: 1371160741856
>>> Description: The time now
>>> THE LONG: 1371160741856
>>> objectClass: [Ljava.lang.String;@373984fd
>>> provides: com.example.markoffline.model.**Model
>>> service.id: 228
>>> ---
>>> --- Here the bundle hits an exception trace, probably not too useful
>>> except
>>> for:
>>> ---
>>> Caused by: java.lang.ClassCastException:
>>> com.example.markoffline.model.**Model cannot be cast to
>>> com.example.markoffline.model.**Model
>>>       at
>>> com.example.markoffline.**consumer.ModelConsumer.start(**
>>> ModelConsumer.java:27)
>>>
>>>
>>>
>>>
>>>
>>>
>>> On 13 June 2013 11:27, Felix Meschberger <fmeschbe@adobe.com> wrote:
>>>
>>>   Hi
>>>> Am 13.06.2013 um 12:18 schrieb Mark Richards:
>>>>
>>>>   Thanks...
>>>>> "In addition the service registry makes sure, that D only gets V1 Model
>>>>> service instances and E only gets V2 Model service instances."
>>>>> I assumed the registry would only care about the versions of Producer
>>>>> and
>>>>> Consumer, as the Models aren't bound directly to the service.
>>>>>
>>>> The service registry does not care for versions at all. It cares to make
>>>> sure a consumer (retrieving the service) can use the service by making
>>>> sure, the consumer and the service object see the same service object
>>>> class
>>>> objects and thus are compatible.
>>>>
>>>> Regards
>>>> Felix
>>>>
>>>>
>>>>> On 13 June 2013 09:56, Felix Meschberger <fmeschbe@adobe.com> wrote:
>>>>>
>>>>>   Hi
>>>>>> OSGi wires API by import and export package version.
>>>>>>
>>>>>> In you example of two incompatible model classes, you might have
bundle
>>>>>>
>>>>> B
>>>>> export the Model API at version 1.0 and bundle C export the Model API
at
>>>>>> version 2.0.
>>>>>>
>>>>>> Your consumer bundles D and E would then import the appropriate Model
>>>>>>
>>>>> API
>>>>> versions such as [1,2) and [2,3).
>>>>>> The actual Model class objects are then different for D and E (even
>>>>>>
>>>>> though
>>>>> the fully qualified class name is the same). In addition the service
>>>>>> registry makes sure, that D only gets V1 Model service instances
and E
>>>>>>
>>>>> only
>>>>> gets V2 Model service instances.
>>>>>> Hope this helps.
>>>>>>
>>>>>> Regards
>>>>>> Felix
>>>>>>
>>>>>> Am 12.06.2013 um 17:56 schrieb Mark Richards:
>>>>>>
>>>>>>   Hi,
>>>>>>> I'm new to this list and hopefully this question isn't too long...
>>>>>>>
>>>>>> please
>>>>> let me know if you'd like further details.
>>>>>>> Context: I'd like to create a service architecture that represents
>>>>>>>
>>>>>> EIPs,
>>>>> a
>>>>>>> bit like Camel but more OSGi friendly, however the safe wiring
of
>>>>>>>
>>>>>> objects
>>>>> must ensure the producers are wired to consumers that provide and
>>>>>> consumer
>>>>>>
>>>>>>> compatible versions of the objects.
>>>>>>>
>>>>>>> Problem: How do you handle the versions dependencies of objects
used a
>>>>>>> layer beneath the services provided?
>>>>>>>
>>>>>>> I believe this issue also affects the listener model, if you
wanted to
>>>>>>>
>>>>>> use
>>>>>>
>>>>>>> a generic listener model like so I'm hoping somebody has already
>>>>>>> solved
>>>>>>>
>>>>>> it:
>>>>>>
>>>>>>> interface Listener<T> { //Listener class is handled by
bundle
>>>>>>>
>>>>>> resolution
>>>>>     void Notify(T event); //but what about this T?
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>> When you create an implementation you usually know T, so it's
quite
>>>>>>>
>>>>>> easy
>>>>> to
>>>>>>> call "bundleContext.register(**Listener.class, this, props)"
where
>>>>>>> props
>>>>>>> includes {"listenerOf":"t.class}" and to then filter the class.
>>>>>>>
>>>>>> However,
>>>>> I'm not sure where to get the version for T from (it may be an imported
>>>>>>> class) and versions are ranges with different rules for import
and
>>>>>>>
>>>>>> export.
>>>>>>
>>>>>>> Any help advice or also, an idea of whether I'm the only one
trying to
>>>>>>>
>>>>>> do
>>>>> this would be useful.
>>>>>>> Thanks,
>>>>>>> Mark
>>>>>>>
>>>>>>>
>>>>>>> Ps: if it helps here is an example of the style of problem in
>>>>>>>
>>>>>> semi-pseudo
>>>>> code.
>>>>>>> If you cannot filter the version of the Model through the services
>>>>>>>
>>>>>> layer
>>>>> ModelProvider will provide a Date object and ModelConsumer will try to
>>>>>> read
>>>>>>
>>>>>>> a Long. The two shouldn't be allowed to bind to each other.
>>>>>>>
>>>>>>>
>>>>>>> bundle A: wiring-api
>>>>>>>     interface Provider<T> {
>>>>>>>         T giveMeAnObject();
>>>>>>>     }
>>>>>>>     interface Consumer<T> {
>>>>>>>         void hereIsAnObject(T object);
>>>>>>>     }
>>>>>>>
>>>>>>> bundle B: model version 1:
>>>>>>>     class Model {
>>>>>>>         long getDate();{ return (long) 99 }
>>>>>>>     }
>>>>>>>
>>>>>>> bundle C: model version 2:
>>>>>>>     class Model {
>>>>>>>         Date getDate() { return new Date(); }
>>>>>>>     }
>>>>>>>
>>>>>>> bundle D: model-provder version 2: import-packages wiring-api
1 and
>>>>>>>
>>>>>> model 2
>>>>>>
>>>>>>>     class ModelProvider implements Provider <Model>  implements
>>>>>>> BundleActivator { {
>>>>>>>         void start(BundleContext context) {
>>>>>>>             context.register(Provider.**class, this, ???? )*
// here
>>>>>>> you'd
>>>>>>> need to specify it is model 2, but how do you get this*
>>>>>>>        }
>>>>>>>         Model giveMeAnObject() {
>>>>>>>             return new Model();
>>>>>>>         }
>>>>>>>     }
>>>>>>> bundle E: consumer imports wiring-api and model 1:
>>>>>>>     class ModelConsumer implements Consumer<Model>, BundleActivator
{
>>>>>>>         void start(BundleContext context) {
>>>>>>>             context.register(Consumer.**class, this, ???? )*
// here
>>>>>>> you'd
>>>>>>> need to specify it is model 1, but how do you get this**
>>>>>>> *        }*
>>>>>>> *
>>>>>>>
>>>>>>>         void hereIsAnObject(Model object) {
>>>>>>>             System.out.println(Long.**valueOf(model.getDate()));
//
>>>>>>> if no
>>>>>>> object version filtering this will fail as bundle D should not
be
>>>>>>>
>>>>>> connected
>>>>>>
>>>>>>> to bundle E
>>>>>>>         }
>>>>>>>     }
>>>>>>>
>>>>>>> // here I envisage there should be a Provider Manager perhaps
along
>>>>>>> the
>>>>>>> lines of that could be exposed to have wiring done through a
UI,
>>>>>>>
>>>>>> interface
>>>>>>
>>>>>>> or config settings like WireAdmin
>>>>>>> // this isn't too important at this point, except it would need
some
>>>>>>>
>>>>>> way
>>>>> to
>>>>>>> manage this *version filtering/matching*!
>>>>>>> bundle F: provider-service imports wiring-api
>>>>>>>     class ProviderManager {
>>>>>>>         Map<ClassVersion, Provider> providers;
>>>>>>>         Map<ClassVersion, Consumer, consumers
>>>>>>>         void registerProvider(ClassVersion<**T> provided,
Provider<T>
>>>>>>> provider);
>>>>>>>         void registerConsumer(ClassVersion<**T> consumed,
Consumer<T>
>>>>>>> provider);
>>>>>>>
>>>>>>>        // methods to lookup and match providers with consumers
>>>>>>>     }
>>>>>>>
>>>>>> ------------------------------**------------------------------**
>>>>>> ---------
>>>>>> To unsubscribe, e-mail: users-unsubscribe@felix.**apache.org<users-unsubscribe@felix.apache.org>
>>>>>> For additional commands, e-mail: users-help@felix.apache.org
>>>>>>
>>>>>>
>>>>>>
>>>> ------------------------------**------------------------------**
>>>> ---------
>>>> To unsubscribe, e-mail: users-unsubscribe@felix.**apache.org<users-unsubscribe@felix.apache.org>
>>>> For additional commands, e-mail: users-help@felix.apache.org
>>>>
>>>>
>>>>
>> ------------------------------**------------------------------**---------
>> To unsubscribe, e-mail: users-unsubscribe@felix.**apache.org<users-unsubscribe@felix.apache.org>
>> For additional commands, e-mail: users-help@felix.apache.org
>>
>>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org


Mime
View raw message