You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@chemistry.apache.org by "Sascha Homeier (JIRA)" <ji...@apache.org> on 2015/01/30 02:26:36 UTC

[jira] [Comment Edited] (CMIS-878) Allow loading classes from other OSGi Bundles in OSGi Client Wrapper

    [ https://issues.apache.org/jira/browse/CMIS-878?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14298034#comment-14298034 ] 

Sascha Homeier edited comment on CMIS-878 at 1/30/15 1:26 AM:
--------------------------------------------------------------

After some research I like the suggestion of [~fmeschbe] in CMIS-324 most to use the OSGi extender model to get the Bundles which contain SPI implementations.
But instead of injecting the class loaders of these Bundles we also can consume the SPI implementations directly as real OSGI services.

For both options I can think of the following:

h4. *1) Get the ClassLoader from other OSGI Bundles*
Inside the Chemistry OpenCmis OSGI Client Wrapper the Activator needs to be adjusted to add a Bundle Listener which listens for Bundle changes (additional logic can be added to also iterate over already loaded bundles in case Chemistry Client Wrapper was started later then SPI Implementation Bundles).
If a bundle is in proper state (e.g. RESOLVED) we should be able to get the Bundle Classloader via BundleWiring:

{code:title=Activator.java|borderStyle=solid}
…
public void bundleChanged(BundleEvent event) {
        if (event.getType() == BundleEvent.RESOLVED) {
	    BundleWiring bundleWiring = event.getBundle().adapt(BundleWiring.class);
	    ClassLoader classLoader = bundleWiring.getClassLoader();
            // pass the class loader to ClassLoaderUtil
        }
}
…
{code}

This ClassLoader then can be passed to ClassLoaderUtil and when it tries to load a class it first uses current behaviour by trying to load the class from context class loader. If this failed it tries loading class by iterating over List of injected class loaders.

We still need to define how to tag the Bundles which contain OpenCmis SPI implementations. For example it could be done by setting a Manifest header like
{{Chemistry-SPI}}.
In Activator we then can get the header via:
{code:title=Activator.java|borderStyle=solid}
...
        if (event.getType() == BundleEvent.RESOLVED) {
            String header = (String) event.getBundle().getHeaders().get("Chemistry-SPI");
            // if header is not null get the class loader via BundleWiring interface and pass it to ClassLoaderUtil
{code}

A better option may be to put a property.

h4. *2) Loading SPI Implementations from other OSGI Bundles*

As OpenCmis does not use java.util.ServiceLoader  I would prefer to provide SPI services as real OSGI services instead of specifying them in files META-INF/services/<FQCN_OF_SPI>.
The OSGI services then can be accessed in Activator of OpenCmis Client Wrapper. For example AuthenticationProvider can be retrieved via:
{code:title=Activator.java|borderStyle=solid}
public void start(BundleContext context) {
    ...
    ServiceReference<?> serviceReference = context.getServiceReference(org.apache.chemistry.opencmis.commons.spi.AuthenticationProvider.class.getName());
    AuthenticationProvider authProvider = (AuthenticationProvider) context.getService(serviceReference); 
    // pass authProvider to proper places in OpenCmis Client framework
}
{code}

This would make the Client library of OpenCmis much more OSGI compliant but is also the most intrusive option:
It requires to pass the SPI implementations to different locations in OpenCmis framework. For example AuthenticationProvider implementation could be passed to CmisBindingFactory while an own Cache implementation is needed in SessionImpl. When injecting ClassLoaders instead of explicit services we can pass them to a single location: ClassLoaderUtil.

I'm interested in what the OpenCmis developers think about these options?
Do you see other alternatives or improvements?


was (Author: shomeier):
After some research I like the suggestion of [~fmeschbe] most to use the OSGi extender model to get the Bundles which contain SPI implementations.
But instead of injecting the class loaders of these Bundles we also can consume the SPI implementations directly as real OSGI services.

For both options I can think of the following:

h4. *1) Get the ClassLoader from other OSGI Bundles*
Inside the Chemistry OpenCmis OSGI Client Wrapper the Activator needs to be adjusted to add a Bundle Listener which listens for Bundle changes (additional logic can be added to also iterate over already loaded bundles in case Chemistry Client Wrapper was started later then SPI Implementation Bundles).
If a bundle is in proper state (e.g. RESOLVED) we should be able to get the Bundle Classloader via BundleWiring:

{code:title=Activator.java|borderStyle=solid}
…
public void bundleChanged(BundleEvent event) {
        if (event.getType() == BundleEvent.RESOLVED) {
	    BundleWiring bundleWiring = event.getBundle().adapt(BundleWiring.class);
	    ClassLoader classLoader = bundleWiring.getClassLoader();
            // pass the class loader to ClassLoaderUtil
        }
}
…
{code}

This ClassLoader then can be passed to ClassLoaderUtil and when it tries to load a class it first uses current behaviour by trying to load the class from context class loader. If this failed it tries loading class by iterating over List of injected class loaders.

We still need to define how to tag the Bundles which contain OpenCmis SPI implementations. For example it could be done by setting a Manifest header like
{{Chemistry-SPI}}.
In Activator we then can get the header via:
{code:title=Activator.java|borderStyle=solid}
...
        if (event.getType() == BundleEvent.RESOLVED) {
            String header = (String) event.getBundle().getHeaders().get("Chemistry-SPI");
            // if header is not null get the class loader via BundleWiring interface and pass it to ClassLoaderUtil
{code}

A better option may be to put a property.

h4. *2) Loading SPI Implementations from other OSGI Bundles*

As OpenCmis does not use java.util.ServiceLoader  I would prefer to provide SPI services as real OSGI services instead of specifying them in files META-INF/services/<FQCN_OF_SPI>.
The OSGI services then can be accessed in Activator of OpenCmis Client Wrapper. For example AuthenticationProvider can be retrieved via:
{code:title=Activator.java|borderStyle=solid}
public void start(BundleContext context) {
    ...
    ServiceReference<?> serviceReference = context.getServiceReference(org.apache.chemistry.opencmis.commons.spi.AuthenticationProvider.class.getName());
    AuthenticationProvider authProvider = (AuthenticationProvider) context.getService(serviceReference); 
    // pass authProvider to proper places in OpenCmis Client framework
}
{code}

This would make the Client library of OpenCmis much more OSGI compliant but is also the most intrusive option:
It requires to pass the SPI implementations to different locations in OpenCmis framework. For example AuthenticationProvider implementation could be passed to CmisBindingFactory while an own Cache implementation is needed in SessionImpl. When injecting ClassLoaders instead of explicit services we can pass them to a single location: ClassLoaderUtil.

I'm interested in what the OpenCmis developers think about these options?
Do you see other alternatives or improvements?

> Allow loading classes from other OSGi Bundles in OSGi Client Wrapper
> --------------------------------------------------------------------
>
>                 Key: CMIS-878
>                 URL: https://issues.apache.org/jira/browse/CMIS-878
>             Project: Chemistry
>          Issue Type: Improvement
>          Components: opencmis-client
>    Affects Versions: OpenCMIS 0.12.0
>         Environment: OSGi
>            Reporter: Sascha Homeier
>            Priority: Minor
>
> When using the OpenCMIS OSGi Client Wrapper it is hard to load classes from other bundles. For example if you specify an own Authentication Provider class as Session Parameter then the Wrapper will not find this class when it is located inside another bundle. Same problem should occur when defining an own Cache.
> *1)*
> It would be nice if other bundles could register their Classloaders so that ClassLoaderUtil can use it when trying to load classes.
> *2)*
> Another simpler option would be to simply add "DynamicImport-Package: *" to the Manifest of the Wrapper. By doing this the Wrapper will find all classes which are exported by other bundles. Though this approach feels more like a hack since it breaks modularity.
> What do you think about it?
> Cheers
> Sascha



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)