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