chemistry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Sascha Homeier (JIRA)" <j...@apache.org>
Subject [jira] [Comment Edited] (CMIS-878) Allow loading classes from other OSGi Bundles in OSGi Client Wrapper
Date Fri, 30 Jan 2015 11:30:35 GMT

    [ 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 11:30 AM:
---------------------------------------------------------------

After some research I like the suggestion of [~fmeschbe] in CMIS-324 most to use the OSGi
extender model to register the class loaders of SPI provider bundles.
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.
When Bundle gets uninstalled of course we need to unregister the class loader from ClassLoaderUtil.
So we need two additional static methods {{registerClassLoader}} and {{unregisterClassLoader}}
at ClassLoaderUtil.

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?

Edit:
After some rethought I think option 2.) is really too intrusive.
It requires to think about how to handle the SPI class names passed via SessionParameter:
Should OSGi services totally override them or should it be coupled?
For now my main interest would be that the library is usable in OSGi environment out-of-the-box
without again wrapping and adjusting it. By option 1) we could at least achieve that for client
side without much effort.


was (Author: shomeier):
After some research I like the suggestion of [~fmeschbe] in CMIS-324 most to use the OSGi
extender model to register the class loaders of SPI provider bundles.
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.
When Bundle gets uninstalled of course we need to unregister the class loader from ClassLoaderUtil.
So we need two additional static methods {{registerClassLoader}} and {{unregisterClassLoader}}
at ClassLoaderUtil.

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)

Mime
View raw message