On Mon, Sep 3, 2018 at 9:27 AM Tim Ward <tim.w...@paremus.com> wrote:

>
>
> On 3 Sep 2018, at 12:46, Alain Picard <pic...@castortech.com> wrote:
>
> Tim,
>
> Regarding point 1, I feel like I dropped the ball and it makes sense.
>
>
> Not really, there genuinely is a bug in bnd 4.0, and it was only fixed in
> the 4.1 development stream recently (within the last week).
>
>
> Regarding point 2, I learned something about the use of converters to
> manage component property types (and not to use them anywhere). So I read
> in chapter 707 to find out how to do it and now I have an issue with my
> Class method, which throws an exception. The priority which is an integer
> works fine.
>
> Code:
>     @Reference
>     private void addRenderer(ConfiguredComponent scc, Map<String,Object>
> props) {
>         this.scc = scc;
>         Converter converter = Converters.standardConverter();
>         RendererConfig config =
> converter.convert(props).to(RendererConfig.class);
>
>         System.out.println("Adding renderer with Tester priority:" +
> config.tester_priority() + "full props" + props);
>         System.out.println("Tester:" + config.tester_class());
>     }
>
> And the exception is:
> java.lang.NoClassDefFoundError: comp.property.test.RendererFactory
>     at
> org.osgi.util.converter.ConverterImpl.loadClassUnchecked(ConverterImpl.java:352)
>     at
> org.osgi.util.converter.ConverterImpl$19.apply(ConverterImpl.java:157)
>     at
> org.osgi.util.converter.ConverterImpl$19.apply(ConverterImpl.java:154)
>     at org.osgi.util.converter.Rule$1.apply(Rule.java:67)
>     at
> org.osgi.util.converter.CustomConverterImpl$ConvertingWrapper.to(CustomConverterImpl.java:162)
>     at
> org.osgi.util.converter.ConvertingImpl$4.invoke(ConvertingImpl.java:809)
>     at com.sun.proxy.$Proxy3.tester_class(Unknown Source)
>     at
> comp.property.test.RendererFactory.addRenderer(RendererFactory.java:23)
>
> And the "swallowed exception is:
> java.lang.ClassNotFoundException: comp.property.test.RendererFactory not
> found by org.osgi.util.converter [6]
>
> org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1597)
>
> org.apache.felix.framework.BundleWiringImpl.access$300(BundleWiringImpl.java:79)
>
> org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1982)
> java.lang.ClassLoader.loadClass(Unknown Source)
>
> org.osgi.util.converter.ConverterImpl.loadClassUnchecked(ConverterImpl.java:350)
>
> any idea or is this is bug or a what.
>
>
> This is not a bug. Classes are “difficult” types as they cannot be easily
> loaded without knowing the module that should provide them. This is one of
> the many reasons that class names are discouraged throughout OSGi. In this
> case the service property that you’re trying to convert is a String class
> name, and the Converter is trying to load that class using its own class
> loader (it doesn’t have any other options). In Java EE they use the Thread
> Context ClassLoader as a hack to get around this, setting that to the
> “correct” ClassLoader to use when trying to load types from the application
> in server runtime code.
>
> You have several options to work around this.
>
>
>    1. Your best option is not to use a class name as a service property
>    at all, and avoid using the Class object. Reflectively loading classes is
>    the beginning of much pain in OSGi, and using a different solution usually
>    results in much better overall modularity.
>
> A bit of the chicken and egg problem. In converting from Eclipse extension
point I went from a "type safe" (at least tooling checked) class name in
plugin.xml to a string property that is unchecked and won't be auto-renamed
if changed. This solution attempts to address the issue of avoiding to use
a string.

>
>    1. If you really do need access to the Class (why?) then return it
>    from a method on the service so that it can be loaded by a bundle who knows
>    how and where to load it from. This still leaves you with potential issues,
>    but should be workable
>
> Why? Since I'm using a 3rd party library that pretty much expects it that
way and some legacy layers.

>
>    1. Add a custom rule to your converter instance that uses the
>    “correct” bundle class loader to load the class. This may be your own
>    (potential for missing imports) the bundle that registered the service
>    (potential for missing uses) or some other bundle (good luck finding it)!.
>
> I am thinking that here I can just simply do props.get("tester.class");
and get the full class name and call our code that already replicates the
behavior used for extension point to createExecutableExtension and that
calls bundle.loadClass. I then get the type safety at dev time, and I can
always manage the loading at runtime.

Thanks
Alain

>
> Best Regards,
>
> Tim
>
>
> Thanks
> Alain
>
>
> On Mon, Sep 3, 2018 at 6:12 AM Tim Ward <tim.w...@paremus.com> wrote:
>
>>
>> Problem 1:
>>
>> The Relevant bitts of the specification are:
>>
>> How component properties override each other at runtime:
>>
>> https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#service.component-component.properties
>>
>> How component properties override each other at build time:
>>
>> https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#service.component-ordering.generated.properties
>>
>> The sum total of this is that the component properties from the
>> annotation that you’ve applied to your component class should come *after*
>> the ones from the activate method. There was a very recent fix in Bnd
>> <https://github.com/bndtools/bnd/pull/2595> to make sure that this was
>> done correctly.
>>
>>
>> Problem 2:
>>
>> As for your additional issue - A component property type is not a valid
>> input for method injection with references. See
>> https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#service.component-method.injection
>>
>> You can use the OSGi converter to convert an injected map of properties
>> into an instance the annotation if you want.
>>
>> Best Regards,
>>
>> Tim
>>
>> On 25 Aug 2018, at 12:37, Alain Picard <pic...@castortech.com> wrote:
>>
>> I had an idea to try using @ComponentPropertyType and searched and found
>> that while not specifically covered by the documentation, in the cpmn there
>> is at least one case of a Class property in ExportedService and that the
>> code seems to support it
>> (ComponentPropertyTypeDataCollector#valueToProperty)
>>
>> So I went ahead and tried it and it works, but I'm having a more general
>> issue with the ComponentProperty type. I am attaching the test project.
>>
>> The encountered issues are twofold. First what is happening is that the
>> values supplied to the annotation are seen in the component, are seen in
>> the reference method of the using service, but not in its activate method,
>> where I get the default values from the annotation. Second, my service that
>> references the one annotated with my component annotation won't start if I
>> use a method signature that references the annotation type instead of using
>> a map.
>>
>> Please enlighten me.
>>
>> Alain
>>
>>
>> On Fri, Aug 24, 2018 at 5:52 AM Tim Ward <tim.w...@paremus.com> wrote:
>>
>>> Right, so in this case it looks like you’re running a whiteboard, is it
>>> possible you would be better off not using the service properties for this
>>> filtering? For example:
>>>
>>> @Reference(policy=DYNAMIC, cardinality=MULTIPLE)
>>> private final List<ZKRenderer> renderers = new CopyOnWriteArrayList<>();
>>>
>>> public ZKRenderer getRendererFor(Object o) {
>>>     return renderers.stream()
>>>         .filter(r -> r.supports(o))
>>>         .collect(Collectors.maxBy((a,b) ->
>>> a.getPriority(o).compareTo(b.getPriority(o))))
>>>         .orElseThrow(() -> new IllegalArgumentException("No renderer for
>>> object " + o));
>>> }
>>>
>>> Tim
>>>
>>> On 24 Aug 2018, at 10:34, Alain Picard <pic...@castortech.com> wrote:
>>>
>>> They represent classes, which is why I would have like to have a Class
>>> annotation so I could do "tester=MyTester.class". instead of
>>> "tester="com.acme.mypkg.MyTester".
>>>
>>> For example I have a number of components implementing a service and as
>>> part of their property they define their "filter conditions" which are then
>>> passed on to the 3rd party library, and there are 2 types of testers, etc:
>>> Component(service=ZKRenderer.class, factory=ZKRenderer.CONFIG_FACTORY,
>>>   property= { ZKRenderer.CONFIG_STATIC_TEST +
>>> "=c.c.i.tester.ReferenceTree",
>>>               ZKRenderer.CONFIG_STATIC_TEST_PRIORITY + ":Integer=9" })
>>>
>>> If I move my ReferenceTree tester in the above case, no compiler would
>>> catch it and I'm just looking for pain in the future.
>>>
>>> I am not sure I grasp your approach. Here clients just ask for a
>>> renderer (an instance of the service) for some "object" that is passed in
>>> and an appropriate and "highest ranking" one is returned. So the client is
>>> never specifying the class string at all. Here we are providing the full
>>> class name so it can be loaded, hence it would be much more natural to
>>> provide a Class object.
>>>
>>> When we have cases where the component and reference must have to match
>>> we do as such:
>>>     public static final String CONFIG_QUALIFIER =
>>> OsgiConstants.SERVICE_QUALIFIER + "=ReferenceList"; //$NON-NLS-1$
>>>     public static final String CONFIG_TARGET = "(" + CONFIG_QUALIFIER +
>>> ")"; //$NON-NLS-1$ //$NON-NLS-2$
>>>
>>> and here the component use the 1st line in its property and the
>>> reference target uses the 2nd constant and that is not an issue.
>>>
>>> Alain
>>>
>>>
>>>
>>> Alain Picard
>>> Chief Strategy Officer
>>> Castor Technologies Inc
>>> o:514-360-7208
>>> m:813-787-3424
>>>
>>> pic...@castortech.com
>>> www.castortech.com
>>>
>>>
>>> On Fri, Aug 24, 2018 at 5:16 AM Tim Ward <tim.w...@paremus.com> wrote:
>>>
>>>> Do these properties “represent” classes or are they actually classes?
>>>> If they are just representations (which would be a good thing) then you can
>>>> define a static string constant representing the class which is mapped
>>>> internally to the correct class name (which can then change over time).
>>>> Clients then filter based on the string representation which will not
>>>> change.
>>>>
>>>> Tim
>>>>
>>>>
>>>> On 24 Aug 2018, at 10:07, Alain Picard <pic...@castortech.com> wrote:
>>>>
>>>> Tim & all,
>>>>
>>>> My immediate use case is that my components have some properties and
>>>> some of those represent classes (this interfaces with 3rd party libraries,
>>>> I would probably design it differently if I could, but it has to be
>>>> configuration as it is used to determine if the component is a match, much
>>>> like for target filters). Properties in the component annotation are
>>>> String[] and that forces the specification of classes as String which is
>>>> very bad since if the class is moved, renamed, deleted, etc, it will cause
>>>> no error or warning and blow up later on. And since annotations only
>>>> support compile time constants, you can't do a MyClass.class.getName() to
>>>> even get a String. My idea was since the implementation class is part of
>>>> the component description, if I could get a hold of it, to have a static
>>>> method in the class to provide this "constant".
>>>>
>>>> How can I work around the limitations of Properties as String and Java
>>>> compile time constants. Am I stuck to introduce a new separate annotation
>>>> to track this configuration?
>>>>
>>>> Alain
>>>>
>>>> Alain
>>>>
>>>>
>>>> On Thu, Aug 23, 2018 at 5:24 AM Tim Ward <tim.w...@paremus.com> wrote:
>>>>
>>>>> The properties visible in the Map (or ServiceReference) are the
>>>>> service properties. There is some overlap with configuration (services 
>>>>> that
>>>>> are configurable are encouraged to publish configuration properties as
>>>>> service properties) but they are separate, and can be different.
>>>>>
>>>>> The only way that something becomes a service property is if it is
>>>>> deliberately registered as such or, for a few specific properties such as
>>>>> service.id and service.scope, added automatically by the framework.
>>>>>
>>>>> The class name of the implementation would only be added as a service
>>>>> property if done so deliberately, and this is typically discouraged (it
>>>>> leaks internal implementation detail and forces your internal naming to
>>>>> become API). If you *really* care about the details of a service (and in
>>>>> general you shouldn’t) then you should mark it with a service property 
>>>>> that
>>>>> you can recognise. Ideally one that is separate from the other
>>>>> implementation details of the service.
>>>>>
>>>>> Best Regards,
>>>>>
>>>>> Tim
>>>>>
>>>>> > On 22 Aug 2018, at 16:53, Alain Picard via osgi-dev <
>>>>> osgi-dev@mail.osgi.org> wrote:
>>>>> >
>>>>> > In a reference method, i can get the property configuration of the
>>>>> service along with the ComponentFactory and some other optional arguments.
>>>>> Can any of those give me a way to retrieve the implementation from the
>>>>> configuration (i.e. the class name of the implementation) ?
>>>>> >
>>>>> > Thanks
>>>>> > Alain
>>>>> >
>>>>> > _______________________________________________
>>>>> > OSGi Developer Mail List
>>>>> > osgi-dev@mail.osgi.org
>>>>> > https://mail.osgi.org/mailman/listinfo/osgi-dev
>>>>>
>>>>>
>>>>
>>> <comp.property.test.zip>
>>
>>
>>
>
_______________________________________________
OSGi Developer Mail List
osgi-dev@mail.osgi.org
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to