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

Sascha Homeier edited comment on CMIS-878 at 5/11/15 6:15 PM:
--------------------------------------------------------------

It is much more straightforward than I thought.

My MANIFEST.MF now contains the following line:
{code}
Chemistry-SPI: 
org.apache.chemistry.opencmis.osgi.client.objectfactory.internal.MockObjectFactoryImpl
{code}

In OSGi Activator start():
{code}
public void start(BundleContext context) {
        // register the MetaTypeService now, that we are ready
        Dictionary<String, String> props = new Hashtable<String, String>();
        props.put(Constants.SERVICE_DESCRIPTION, "Apache Chemistry OpenCMIS 
Client Session Factory");
        props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");

        // try to find classloader of Chemistry OpenCMIS ObjectFactory or
        // AuthenticationProvider implementations
        Map<String, ClassLoader> chemistrySpiClassLoader = 
findChemistrySpiClassLoader(context);

        SessionFactoryImpl sessionFactory = 
SessionFactoryImpl.newInstance(chemistrySpiClassLoader);

        context.registerService(SessionFactory.class.getName(), sessionFactory, 
props);
 }

private Map<String, ClassLoader> findChemistrySpiClassLoader(BundleContext 
context) {

        Map<String, ClassLoader> classLoaderMap = new HashMap<String, 
ClassLoader>();

        // get all currently loaded bundles
        // TODO: How to avoid playing with start-levels to ensure that 
chemistry spi bundles are loaded first 
        Bundle[] bundles = context.getBundles();
        for (Bundle bundle : bundles) {
            
            // if chemistry spi header is set store the classloader(s) of the 
bundle with key = class name
            String value = (String) 
bundle.getHeaders().get(CHEMISTRY_SPI_HEADER);
           if (value != null) {
                String[] split = value.split(",");
                for (String className : split) {

                    if ((className != null) && (!className.trim().isEmpty())) {
                        BundleWiring bundleWiring = 
bundle.adapt(BundleWiring.class);
                        ClassLoader classLoader = bundleWiring.getClassLoader();
                        classLoaderMap.put(className.trim(), classLoader);
                    }
                }
            }
        }

        return classLoaderMap;
 }
{code}

In SessionFactoryImpl:
{code}
protected SessionFactoryImpl(Map<String, ClassLoader> chemistrySpiClassLoader) {
        this.chemistrySpiClassLoader = chemistrySpiClassLoader;
}

public static SessionFactoryImpl newInstance(Map<String, ClassLoader> 
chemistrySpiClassLoader) {
        return new SessionFactoryImpl(chemistrySpiClassLoader);
}

public Session createSession(Map<String, String> parameters) {
        return createSession(parameters, getObjectFactory(parameters), 
getAuthenticationProvider(parameters), null,
                null);
}

// OSGi
// the following methods try to load ObjectFactory and
// AuthenticationProvider via ClassLoader injected by OSGi Activator

private ObjectFactory getObjectFactory(Map<String, String> parameters) {

        if (parameters.containsKey(SessionParameter.OBJECT_FACTORY_CLASS)) {

            String ofClassName = 
parameters.get(SessionParameter.OBJECT_FACTORY_CLASS);
            if ((this.chemistrySpiClassLoader != null) && 
(this.chemistrySpiClassLoader.containsKey(ofClassName))) {
                ClassLoader classLoader = 
this.chemistrySpiClassLoader.get(ofClassName);

                Object objectFactory;
                try {
                    objectFactory = ClassLoaderUtil.loadClass(ofClassName, 
classLoader).newInstance();
                } catch (Exception e) {
                    throw new IllegalArgumentException("Could not load object 
factory: " + e, e);
                }

                if (!(objectFactory instanceof ObjectFactory)) {
                    throw new IllegalArgumentException("Object factory does not 
implement ObjectFactory!");
                }
                return (ObjectFactory) objectFactory;
            }
        }

        return null;
 }

// same for AuthenticationProvider
...
{code}
(of course I did not remove existing no-arg newInstance()...just skipped it 
here for brevity)

So any vendor of an AuthenticationProvider and/or ObjectFactory now needs to do 
two things inside an OSGi environment:
* Put the following line inside the Manifest: "Chemistry-SPI: <classname>"
* When creating the session pass the classname as SessionParameter

When one bundle holds both AuthenticationProvider and ObjectFactory then you 
need to separate the class names by comma inside manifest.
Every ClassLoader is stored inside the SessionFactoryImpl in a Map with key = 
class name.

Just a rough outline to illustrate the approach.
What do you think?

(I completely removed both Caches cause it is not possible to implement them 
when interface is internal)


was (Author: shomeier):
It is much more straightforward than I thought.

My MANIFEST.MF now contains the following line:
{code}
Chemistry-SPI: 
org.apache.chemistry.opencmis.osgi.client.objectfactory.internal.MockObjectFactoryImpl
{code}

In OSGi Activator start():
{code}
public void start(BundleContext context) {
        // register the MetaTypeService now, that we are ready
        Dictionary<String, String> props = new Hashtable<String, String>();
        props.put(Constants.SERVICE_DESCRIPTION, "Apache Chemistry OpenCMIS 
Client Session Factory");
        props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");

        // try to find classloader of Chemistry OpenCMIS ObjectFactory or
        // AuthenticationProvider implementations
        Map<String, ClassLoader> chemistrySpiClassLoader = 
findChemistrySpiClassLoader(context);

        SessionFactoryImpl sessionFactory = 
SessionFactoryImpl.newInstance(chemistrySpiClassLoader);

        context.registerService(SessionFactory.class.getName(), sessionFactory, 
props);
 }

private Map<String, ClassLoader> findChemistrySpiClassLoader(BundleContext 
context) {

        Map<String, ClassLoader> classLoaderMap = new HashMap<String, 
ClassLoader>();

        // get all currently loaded bundles
        // TODO: How to avoid playing with start-levels to ensure that 
chemistry spi bundles are loaded first 
        Bundle[] bundles = context.getBundles();
        for (Bundle bundle : bundles) {
            
            // if chemistry spi header is set store the classloader(s) of the 
bundle with key = class name
            String value = (String) 
bundle.getHeaders().get(CHEMISTRY_SPI_HEADER);
            String[] split = value.split(",");
            for (String className : split) {

                if ((className != null) && (!className.trim().isEmpty())) {
                    BundleWiring bundleWiring = 
bundle.adapt(BundleWiring.class);
                    ClassLoader classLoader = bundleWiring.getClassLoader();
                    classLoaderMap.put(className.trim(), classLoader);
                }
            }
        }

        return classLoaderMap;
 }
{code}

In SessionFactoryImpl:
{code}
protected SessionFactoryImpl(Map<String, ClassLoader> chemistrySpiClassLoader) {
        this.chemistrySpiClassLoader = chemistrySpiClassLoader;
}

public static SessionFactoryImpl newInstance(Map<String, ClassLoader> 
chemistrySpiClassLoader) {
        return new SessionFactoryImpl(chemistrySpiClassLoader);
}

public Session createSession(Map<String, String> parameters) {
        return createSession(parameters, getObjectFactory(parameters), 
getAuthenticationProvider(parameters), null,
                null);
}

// OSGi
// the following methods try to load ObjectFactory and
// AuthenticationProvider via ClassLoader injected by OSGi Activator

private ObjectFactory getObjectFactory(Map<String, String> parameters) {

        if (parameters.containsKey(SessionParameter.OBJECT_FACTORY_CLASS)) {

            String ofClassName = 
parameters.get(SessionParameter.OBJECT_FACTORY_CLASS);
            if ((this.chemistrySpiClassLoader != null) && 
(this.chemistrySpiClassLoader.containsKey(ofClassName))) {
                ClassLoader classLoader = 
this.chemistrySpiClassLoader.get(ofClassName);

                Object objectFactory;
                try {
                    objectFactory = ClassLoaderUtil.loadClass(ofClassName, 
classLoader).newInstance();
                } catch (Exception e) {
                    throw new IllegalArgumentException("Could not load object 
factory: " + e, e);
                }

                if (!(objectFactory instanceof ObjectFactory)) {
                    throw new IllegalArgumentException("Object factory does not 
implement ObjectFactory!");
                }
                return (ObjectFactory) objectFactory;
            }
        }

        return null;
 }

// same for AuthenticationProvider
...
{code}
(of course I did not remove existing no-arg newInstance()...just skipped it 
here for brevity)

So any vendor of an AuthenticationProvider and/or ObjectFactory now needs to do 
two things inside an OSGi environment:
* Put the following line inside the Manifest: "Chemistry-SPI: <classname>"
* When creating the session pass the classname as SessionParameter

When one bundle holds both AuthenticationProvider and ObjectFactory then you 
need to separate the class names by comma inside manifest.
Every ClassLoader is stored inside the SessionFactoryImpl in a Map with key = 
class name.

Just a rough outline to illustrate the approach.
What do you think?

(I completely removed both Caches cause it is not possible to implement them 
when interface is internal)

> 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)

Reply via email to