You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2015/05/24 19:02:38 UTC

svn commit: r1681493 - in /chemistry/opencmis/trunk: chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/osgi/ chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/c...

Author: fmui
Date: Sun May 24 17:02:37 2015
New Revision: 1681493

URL: http://svn.apache.org/r1681493
Log:
CMIS-878: loading classes from OSGi bundles

Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/osgi/Activator.java
    chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/ClassLoaderUtil.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/osgi/Activator.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/osgi/Activator.java?rev=1681493&r1=1681492&r2=1681493&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/osgi/Activator.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/osgi/Activator.java Sun May 24 17:02:37 2015
@@ -18,22 +18,52 @@
  */
 package org.apache.chemistry.opencmis.client.osgi;
 
+import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Hashtable;
+import java.util.List;
 
 import org.apache.chemistry.opencmis.client.api.SessionFactory;
 import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
+import org.apache.chemistry.opencmis.commons.impl.ClassLoaderUtil;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
 import org.osgi.framework.Constants;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.wiring.BundleWiring;
 
 /**
  * OSGi Bundle activator for the OpenCMIS client which registers an instance of
  * the {@link SessionFactory} in the OSGi service registry.
  */
-public class Activator implements BundleActivator {
+public class Activator implements BundleActivator, SynchronousBundleListener {
+
+    /**
+     * Represents the manifest header indicating this bundle holds an OpenCMIS
+     * SPI implementation
+     */
+    private static final String OPENCMIS_SPI_HEADER = "OpenCMIS-SPI";
+
+    private BundleContext bundleContext;
+
+    @Override
+    public void start(final BundleContext context) {
+
+        this.bundleContext = context;
+
+        // add bundle listener
+        context.addBundleListener(this);
+
+        // check existing bundles in framework for chemistry SPIs
+        for (Bundle bundle : context.getBundles()) {
+            if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.STARTING
+                    || bundle.getState() == Bundle.ACTIVE || bundle.getState() == Bundle.STOPPING) {
+                register(bundle);
+            }
+        }
 
-    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");
@@ -43,7 +73,72 @@ public class Activator implements Bundle
         context.registerService(SessionFactory.class.getName(), sessionFactory, props);
     }
 
-    public void stop(BundleContext context) {
+    @Override
+    public void stop(final BundleContext context) {
+        // remove bundle listener
+        context.removeBundleListener(this);
+
+        // forget our bundle context
+        bundleContext = null;
+
+        // unregister all classloaders
+        ClassLoaderUtil.unregisterAllBundleClassLoaders();
+
         // The SessionFactory service will be unregistered automatically
     }
+
+    @Override
+    public void bundleChanged(final BundleEvent event) {
+        // bundle context might not yet been initialized
+        synchronized (this) {
+            if (bundleContext == null) {
+                return;
+            }
+        }
+
+        if (event.getType() == BundleEvent.RESOLVED) {
+            register(event.getBundle());
+        } else if (event.getType() == BundleEvent.UNRESOLVED || event.getType() == BundleEvent.UNINSTALLED) {
+            unregister(event.getBundle().getBundleId());
+        }
+    }
+
+    private void register(final Bundle bundle) {
+        BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
+        if (bundleWiring == null) {
+            return;
+        }
+
+        ClassLoader classLoader = bundleWiring.getClassLoader();
+        if (classLoader == null) {
+            return;
+        }
+
+        List<String> classes = getOpenCmisSpiHeader(bundle);
+        if (classes != null) {
+            ClassLoaderUtil.registerBundleClassLoader(bundle.getBundleId(), classLoader, classes);
+        }
+    }
+
+    private void unregister(final long bundleId) {
+        ClassLoaderUtil.unregisterBundleClassLoader(bundleId);
+    }
+
+    private List<String> getOpenCmisSpiHeader(final Bundle bundle) {
+        String spiHeader = (String) bundle.getHeaders().get(OPENCMIS_SPI_HEADER);
+        if (spiHeader == null) {
+            return null;
+        }
+
+        List<String> headerValues = new ArrayList<String>();
+
+        String[] split = spiHeader.split(",");
+        for (String className : split) {
+            if (className != null && !className.trim().isEmpty()) {
+                headerValues.add(className.trim());
+            }
+        }
+
+        return headerValues;
+    }
 }

Modified: chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/ClassLoaderUtil.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/ClassLoaderUtil.java?rev=1681493&r1=1681492&r2=1681493&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/ClassLoaderUtil.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/ClassLoaderUtil.java Sun May 24 17:02:37 2015
@@ -18,24 +18,126 @@
  */
 package org.apache.chemistry.opencmis.commons.impl;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
 public final class ClassLoaderUtil {
 
+    private static ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
+    private static Map<Long, Map<String, ClassLoader>> CLASSLOADER_MAP = null;
+
     private ClassLoaderUtil() {
     }
 
     /**
+     * Registers a bundle classloader.
+     */
+    public static void registerBundleClassLoader(long bundleId, ClassLoader classLoader, List<String> classes) {
+        if (classLoader == null || classes == null || classes.isEmpty()) {
+            return;
+        }
+
+        Map<String, ClassLoader> bundleMap = new HashMap<String, ClassLoader>();
+
+        for (String clazz : classes) {
+            if (clazz != null && !clazz.isEmpty()) {
+                bundleMap.put(clazz, classLoader);
+            }
+        }
+
+        if (bundleMap.isEmpty()) {
+            return;
+        }
+
+        LOCK.writeLock().lock();
+        try {
+            if (CLASSLOADER_MAP == null) {
+                CLASSLOADER_MAP = new HashMap<Long, Map<String, ClassLoader>>();
+            }
+
+            CLASSLOADER_MAP.put(bundleId, bundleMap);
+        } finally {
+            LOCK.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Unregisters a bundle classloader.
+     */
+    public static void unregisterBundleClassLoader(long bundleId) {
+        LOCK.writeLock().lock();
+        try {
+            if (CLASSLOADER_MAP != null) {
+                CLASSLOADER_MAP.remove(bundleId);
+            }
+        } finally {
+            LOCK.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Unregisters all bundle classloaders.
+     */
+    public static void unregisterAllBundleClassLoaders() {
+        LOCK.writeLock().lock();
+        try {
+            CLASSLOADER_MAP = null;
+        } finally {
+            LOCK.writeLock().unlock();
+        }
+    }
+
+    /**
      * Loads a class. If the context class loader is set, it is used.
      */
     public static Class<?> loadClass(String className) throws ClassNotFoundException {
         ClassLoader ccl = Thread.currentThread().getContextClassLoader();
         if (ccl == null) {
-            return loadClass(className, null);
+            try {
+                return loadClass(className, null);
+            } catch (ClassNotFoundException cnf) {
+                return loadClassWithRegisteredClassLoaders(className);
+            }
         }
 
         try {
             return loadClass(className, ccl);
         } catch (ClassNotFoundException cnf) {
-            return loadClass(className, null);
+            try {
+                return loadClass(className, null);
+            } catch (ClassNotFoundException cnf2) {
+                return loadClassWithRegisteredClassLoaders(className);
+            }
+        }
+    }
+
+    /**
+     * Loads a class with the reigistered class loaders.
+     */
+    private static Class<?> loadClassWithRegisteredClassLoaders(String className) throws ClassNotFoundException {
+        if (className == null) {
+            throw new ClassNotFoundException("Class name is null!");
+        }
+
+        LOCK.readLock().lock();
+        try {
+            if (CLASSLOADER_MAP == null) {
+                throw new ClassNotFoundException();
+            }
+
+            for (Map<String, ClassLoader> clm : CLASSLOADER_MAP.values()) {
+                for (Map.Entry<String, ClassLoader> cle : clm.entrySet()) {
+                    if (cle.getKey().equals(className)) {
+                        return loadClass(className, cle.getValue());
+                    }
+                }
+            }
+
+            throw new ClassNotFoundException();
+        } finally {
+            LOCK.readLock().unlock();
         }
     }