geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From djen...@apache.org
Subject [03/51] [abbrv] geronimo-yoko git commit: Cache outbound connections with reference counting.
Date Sun, 19 Feb 2017 01:49:20 GMT
http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderLocator.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderLocator.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderLocator.java
new file mode 100644
index 0000000..1f7d28d
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderLocator.java
@@ -0,0 +1,583 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.yoko.osgi;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+public class ProviderLocator {
+    // a service tracker for the registry service
+    // NB:  This is declared as just Object to avoid classloading issues if we're running
+    // outside of an OSGi environment.
+    static private ProviderRegistry registry;
+
+    private ProviderLocator() {
+        // private constructor to prevent an instance from getting created.
+    }
+
+
+    public static void setRegistry(ProviderRegistry registry) {
+        ProviderLocator.registry = registry;
+    }
+
+    /**
+     * Locate a class by its provider id indicator. .
+     *
+     * @param providerId The provider id (generally, a fully qualified class name).
+     *
+     * @return The Class corresponding to this provider id.  Returns null
+     *         if this is not registered or the indicated class can't be
+     *         loaded.
+     */
+    static public Class<?> locate(String providerId) {
+        ProviderRegistry registry = getRegistry();
+        // if no registry service available, this is a failure
+        if (registry == null) {
+            return null;
+        }
+        // get the service, if it exists.  NB, if there is a service object,
+        // then the extender and the interface class are available, so this cast should be
+        // safe now.
+
+        // the rest of the work is done by the registry
+        return registry.locate(providerId);
+    }
+
+    /**
+     * Locate all class files that match a given factory id.
+     *
+     * @param providerId The target provider identifier.
+     *
+     * @return A List containing the class objects corresponding to the
+     *         provider identifier.  Returns an empty list if no
+     *         matching classes can be located.
+     */
+    static public List<Class<?>> locateAll(String providerId) {
+        Object registry = getRegistry();
+
+        // if no registry service available, this is a failure
+        if (registry == null) {
+            return new ArrayList<Class<?>>();
+        }
+        // get the service, if it exists.  NB, if there is a service object,
+        // then the extender and the interface class are available, so this cast should be
+        // safe now.
+
+        // the rest of the work is done by the registry
+        return ((ProviderRegistry)registry).locateAll(providerId);
+    }
+
+    /**
+     * Utility class for locating a class with OSGi registry
+     * support.  Uses the thread context classloader as part of
+     * the search order.
+     *
+     * @param className The name of the target class.
+     *
+     * @return The loaded class.
+     * @exception ClassNotFoundException
+     *                   Thrown if the class cannot be located.
+     */
+    static public Class<?> loadClass(String className) throws ClassNotFoundException {
+        return loadClass(className, null, Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * Utility class for locating a class with OSGi registry
+     * support.  Uses the thread context classloader as part of
+     * the search order.
+     *
+     * @param className The name of the target class.
+     *
+     * @return The loaded class.
+     * @exception ClassNotFoundException
+     *                   Thrown if the class cannot be located.
+     */
+    static public Class<?> loadClass(String className, Class<?> contextClass) throws ClassNotFoundException {
+        return loadClass(className, contextClass, Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * Standardized utility method for performing class lookups
+     * with support for OSGi registry lookups.
+     *
+     * @param className The name of the target class.
+     * @param loader    An optional class loader.
+     *
+     * @return The loaded class
+     * @exception ClassNotFoundException
+     *                   Thrown if the class cannot be loaded.
+     */
+    static public Class<?> loadClass(String className, Class<?>contextClass, ClassLoader loader) throws ClassNotFoundException {
+        // ideally, this should be last.  However, some of the bundles duplicate classes
+        // found on the boot delegation, so we need to check this first to keep
+        // from picking up one of the default implementations.
+        Class cls = locate(className);
+        if (cls != null) {
+            return cls;
+        }
+
+        if (loader != null) {
+            try {
+                return loader.loadClass(className);
+            } catch (ClassNotFoundException x) {
+            }
+        }
+        if (contextClass != null) {
+            loader = contextClass.getClassLoader();
+        }
+        // try again using the class context loader
+        return Class.forName(className, true, loader);
+    }
+
+
+    /**
+     * Get a single service instance that matches an interface
+     * definition.
+     *
+     * @param iface  The name of the required interface.
+     * @param contextClass
+     *               The class requesting the lookup (used for class resolution).
+     * @param loader A class loader to use for searching for service definitions
+     *               and loading classes.
+     *
+     * @return The service instance, or null if no matching services
+     *         can be found.
+     * @exception Exception Thrown for any classloading or exceptions thrown
+     *                      trying to instantiate a service instance.
+     */
+    static public Object getService(String iface, Class<?> contextClass, ClassLoader loader) throws Exception {
+        // if we are working in an OSGi environment, then process the service
+        // registry first.  Ideally, we would do this last, but because of boot delegation
+        // issues with some API implementations, we must try the OSGi version first
+        Object registry = getRegistry();
+        if (registry != null) {
+            // get the service, if it exists.  NB, if there is a service object,
+            // then the extender and the interface class are available, so this cast should be
+            // safe now.
+            // the rest of the work is done by the registry
+            Object service = ((ProviderRegistry)registry).getService(iface);
+            if (service != null) {
+                return service;
+            }
+        }
+
+        // try for a classpath locatable instance next.  If we find an appropriate class mapping,
+        // create an instance and return it.
+        Class<?> cls = locateServiceClass(iface, contextClass, loader);
+        if (cls != null) {
+            return cls.newInstance();
+        }
+        // a provider was not found
+        return null;
+    }
+
+
+    /**
+     * Locate a service class that matches an interface
+     * definition.
+     *
+     * @param iface  The name of the required interface.
+     * @param contextClass
+     *               The class requesting the lookup (used for class resolution).
+     * @param loader A class loader to use for searching for service definitions
+     *               and loading classes.
+     *
+     * @return The located class, or null if no matching services
+     *         can be found.
+     * @exception Exception Thrown for any classloading exceptions thrown
+     *                      trying to load the class.
+     */
+    static public Class<?> getServiceClass(String iface, Class<?> contextClass, ClassLoader loader) throws ClassNotFoundException {
+        // if we are working in an OSGi environment, then process the service
+        // registry first.  Ideally, we would do this last, but because of boot delegation
+        // issues with some API implementations, we must try the OSGi version first
+        Object registry = getRegistry();
+        if (registry != null) {
+            // get the service, if it exists.  NB, if there is a service object,
+            // then the extender and the interface class are available, so this cast should be
+            // safe now.
+
+            // If we've located stuff in the registry, then return it
+            Class<?> cls = ((ProviderRegistry)registry).getServiceClass(iface);
+            if (cls != null) {
+                return cls;
+            }
+        }
+
+        // try for a classpath locatable instance first.  If we find an appropriate class mapping,
+        // create an instance and return it.
+        return locateServiceClass(iface, contextClass, loader);
+    }
+
+
+    /**
+     * Get a list of services that match a given interface
+     * name.  This searches both the current class path and
+     * the global repository for matches.
+     *
+     * @param iface  The name of the required interface.
+     * @param contextClass
+     *               The class requesting the lookup (used for class resolution).
+     * @param loader A class loader to use for searching for service definitions
+     *               and loading classes.
+     *
+     * @return A list of matching services.  Returns an empty list if there
+     *         are no matches.
+     * @exception Exception Thrown for any classloading or exceptions thrown
+     *                      trying to instantiate a service instance.
+     */
+    static public List<Object> getServices(String iface, Class<?> contextClass, ClassLoader loader) throws Exception {
+        List<Object> services = new ArrayList<Object>();
+
+        // because of boot delegation issues with some of the API implementations, it is necessary
+        // to process the OSGi registered versions first to allow override of JRE provided APIs.
+        Object registry = getRegistry();
+        if (registry != null) {
+            // get the service, if it exists.  NB, if there is a service object,
+            // then the extender and the interface class are available, so this cast should be
+            // safe now.
+            // get any registered service instances now
+            List<Object> globalServices = ((ProviderRegistry)registry).getServices(iface);
+            // add to our list also
+            if (globalServices != null) {
+                services.addAll(globalServices);
+            }
+        }
+
+        // try for a classpath locatable instance second.  If we find an appropriate class mapping,
+        // create an instance and return it.
+        Collection<Class<?>> classes = locateServiceClasses(iface, contextClass, loader);
+        if (classes != null) {
+            // create an instance of each of these classes
+            for (Class<?> cls : classes) {
+                services.add(cls.newInstance());
+            }
+        }
+
+        // now return the merged set
+        return services;
+    }
+
+
+    /**
+     * Get a list of service class implementations that match
+     * an interface name.  This searches both the current class path and
+     * the global repository for matches.
+     *
+     * @param iface  The name of the required interface.
+     * @param contextClass
+     *               The class requesting the lookup (used for class resolution).
+     * @param loader A class loader to use for searching for service definitions
+     *               and loading classes.
+     *
+     * @return A list of matching provider classes.  Returns an empty list if there
+     *         are no matches.
+     * @exception Exception Thrown for any classloading exceptions thrown
+     *                      trying to load a provider class.
+     */
+    static public List<Class<?>> getServiceClasses(String iface, Class<?> contextClass, ClassLoader loader) throws Exception {
+        Set<Class<?>> serviceClasses = new LinkedHashSet<Class<?>>();
+
+        // because of boot delegation issues with some of the API implementations, it is necessary
+        // to process the OSGi registered versions first to allow override of JRE provided APIs.
+        Object registry = getRegistry();
+        if (registry != null) {
+            // get the service, if it exists.  NB, if there is a service object,
+            // then the extender and the interface class are available, so this cast should be
+            // safe now.
+            // get any registered service provider classes now
+            List<Class<?>> globalServices = ((ProviderRegistry)registry).getServiceClasses(iface);
+            // add to our list also
+            if (globalServices != null) {
+                serviceClasses.addAll(globalServices);
+            }
+        }
+
+        // try for a classpath locatable classes second.  If we find an appropriate class mapping,
+        // add this to our return collection.
+        Collection<Class<?>> classes = locateServiceClasses(iface, contextClass, loader);
+        if (classes != null) {
+            serviceClasses.addAll(classes);
+        }
+        // now return the merged set
+        return new ArrayList(serviceClasses);
+    }
+
+
+    /**
+     * Locate the first class name for a META-INF/services definition
+     * of a given class.  The first matching provider is
+     * returned.
+     *
+     * @param iface  The interface class name used for the match.
+     * @param loader The classloader for locating resources.
+     *
+     * @return The mapped provider name, if found.  Returns null if
+     *         no mapping is located.
+     */
+    static private String locateServiceClassName(String iface, Class<?> contextClass, ClassLoader loader) {
+        // search first with the loader class path
+        String name = locateServiceClassName(iface, loader);
+        if (name != null) {
+            return name;
+        }
+        // then with the context class, if there is one
+        if (contextClass != null) {
+            name = locateServiceClassName(iface, contextClass.getClassLoader());
+            if (name != null) {
+                return name;
+            }
+        }
+        // not found
+        return null;
+    }
+
+
+    /**
+     * Locate a classpath-define service mapping.
+     *
+     * @param iface  The required interface name.
+     * @param loader The ClassLoader instance to use to locate the service.
+     *
+     * @return The mapped class name, if one is found.  Returns null if the
+     *         mapping is not located.
+     */
+    static private String locateServiceClassName(String iface, ClassLoader loader) {
+        if (loader != null) {
+            try {
+                // we only look at resources that match the file name, using the specified loader
+                String service = "META-INF/services/" + iface;
+                Enumeration<URL> providers = loader.getResources(service);
+
+                while (providers.hasMoreElements()) {
+                    List<String>providerNames = parseServiceDefinition(providers.nextElement());
+                    // if there is something defined here, return the first entry
+                    if (!providerNames.isEmpty()) {
+                        return providerNames.get(0);
+                    }
+                }
+            } catch (IOException e) {
+            }
+        }
+        // not found
+        return null;
+    }
+
+
+    /**
+     * Locate the first class for a META-INF/services definition
+     * of a given interface class.  The first matching provider is
+     * returned.
+     *
+     * @param iface  The interface class name used for the match.
+     * @param loader The classloader for locating resources.
+     *
+     * @return The mapped provider class, if found.  Returns null if
+     *         no mapping is located.
+     */
+    static private Class<?> locateServiceClass(String iface, Class<?> contextClass, ClassLoader loader) throws ClassNotFoundException {
+        String className = locateServiceClassName(iface, contextClass, loader);
+        if (className == null) {
+            return null;
+        }
+
+        // we found a name, try loading the class.  This will throw an exception if there is an error
+        return loadClass(className, contextClass, loader);
+    }
+
+
+    /**
+     * Locate all class names name for a META-INF/services definition
+     * of a given class.
+     *
+     * @param iface  The interface class name used for the match.
+     * @param loader The classloader for locating resources.
+     *
+     * @return The mapped provider name, if found.  Returns null if
+     *         no mapping is located.
+     */
+    static private Collection<String> locateServiceClassNames(String iface, Class<?> contextClass, ClassLoader loader) {
+        Set<String> names = new LinkedHashSet<String>();
+
+        locateServiceClassNames(iface, loader, names);
+        if (contextClass != null) {
+            locateServiceClassNames(iface, contextClass.getClassLoader(), names);
+        }
+
+        return names;
+    }
+
+
+    /**
+     * Locate all class names name for a META-INF/services definition
+     * of a given class.
+     *
+     * @param iface  The interface class name used for the match.
+     * @param loader The classloader for locating resources.
+     *
+     * @return The mapped provider name, if found.  Returns null if
+     *         no mapping is located.
+     */
+    static void locateServiceClassNames(String iface, ClassLoader loader, Set names) {
+        if (loader != null) {
+
+            try {
+                // we only look at resources that match the file name, using the specified loader
+                String service = "META-INF/services/" + iface;
+                Enumeration<URL> providers = loader.getResources(service);
+
+                while (providers.hasMoreElements()) {
+                    List<String>providerNames = parseServiceDefinition(providers.nextElement());
+                    // just add all of these to the list
+                    names.addAll(providerNames);
+                }
+            } catch (IOException e) {
+            }
+        }
+    }
+
+
+    /**
+     * Locate all classes that map to a given provider class definition.  This will
+     * search both the services directories, as well as the provider classes from the
+     * OSGi provider registry.
+     *
+     * @param iface  The interface class name used for the match.
+     * @param loader The classloader for locating resources.
+     *
+     * @return A list of all mapped classes, if found.  Returns an empty list if
+     *         no mappings are found.
+     */
+    static private Collection<Class<?>> locateServiceClasses(String iface, Class<?> contextClass, ClassLoader loader) throws ClassNotFoundException {
+        // get the set of names from services definitions on the classpath
+        Collection<String> classNames = locateServiceClassNames(iface, contextClass, loader);
+        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
+
+        // load each class and add to our return set
+        for (String name : classNames) {
+            classes.add(loadClass(name, contextClass, loader));
+        }
+        return classes;
+    }
+
+
+    /**
+     * Parse a definition file and return the names of all included implementation classes
+     * contained within the file.
+     *
+     * @param u      The URL of the file
+     *
+     * @return A list of all matching classes.  Returns an empty list
+     *         if no matches are found.
+     */
+    static private List<String> parseServiceDefinition(URL u) {
+        final String url = u.toString();
+        List<String> classes = new ArrayList<String>();
+        // ignore directories
+        if (url.endsWith("/")) {
+            return classes;
+        }
+        // the identifier used for the provider is the last item in the URL.
+        final String providerId = url.substring(url.lastIndexOf("/") + 1);
+        try {
+            BufferedReader br = new BufferedReader(new InputStreamReader(u.openStream(), "UTF-8"));
+            // the file can be multiple lines long, with comments.  A single file can define multiple providers
+            // for a single key, so we might need to create multiple entries.  If the file does not contain any
+            // definition lines, then as a default, we use the providerId as an implementation class also.
+            String line = br.readLine();
+            while (line != null) {
+                // we allow comments on these lines, and a line can be all comment
+                int comment = line.indexOf('#');
+                if (comment != -1) {
+                    line = line.substring(0, comment);
+                }
+                line = line.trim();
+                // if there is nothing left on the line after stripping white space and comments, skip this
+                if (line.length() > 0) {
+                    // add this to our list
+                    classes.add(line);
+                }
+                // keep reading until the end.
+                line = br.readLine();
+            }
+            br.close();
+        } catch (IOException e) {
+            // ignore errors and handle as default
+        }
+        return classes;
+    }
+
+    /**
+     * Perform a service class discovery by looking for a
+     * property in a target properties file located in the
+     * java.home directory.
+     *
+     * @param path     The relative path to the desired properties file.
+     * @param property The name of the required property.
+     *
+     * @return The value of the named property within the properties file.  Returns
+     *         null if the property doesn't exist or the properties file doesn't exist.
+     */
+    public static String lookupByJREPropertyFile(String path, String property) throws IOException {
+        String jreDirectory = System.getProperty("java.home");
+        File configurationFile = new File(jreDirectory + File.separator + path);
+        if (configurationFile.exists() && configurationFile.canRead()) {
+            Properties properties = new Properties();
+            InputStream in = null;
+            try {
+                in = new FileInputStream(configurationFile);
+                properties.load(in);
+                return properties.getProperty(property);
+            } finally {
+                if (in != null) {
+                    try {
+                        in.close();
+                    } catch (Exception e) {
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Retrieve the registry from the tracker if it is available,
+     * all without causing the interface class to load.
+     *
+     * @return The registry service instance, or null if it is not
+     *         available for any reason.
+     */
+    private static ProviderRegistry getRegistry() {
+        return registry;
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderRegistry.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderRegistry.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderRegistry.java
new file mode 100644
index 0000000..3fc69d4
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/ProviderRegistry.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.yoko.osgi;
+
+import java.util.List;
+
+/**
+ * The implementation of the factory registry used to store
+ * the bundle registrations.
+ */
+public interface ProviderRegistry {
+    /**
+     * Locate a class by its factory id indicator. .
+     *
+     * @param factoryId The factory id (generally, a fully qualified class name).
+     *
+     * @return The Class corresponding to this factory id.  Returns null
+     *         if this is not registered or the indicated class can't be
+     *         loaded.
+     */
+    public Class<?> locate(String factoryId);
+
+    /**
+     * Locate all class files that match a given factory id.
+     *
+     * @param factoryId The target factory identifier.
+     *
+     * @return A List containing the class objects corresponding to the
+     *         factory identifier.  Returns an empty list if no
+     *         matching classes can be located.
+     */
+    public List<Class<?>> locateAll(String factoryId);
+
+
+    /**
+     * Locate and instantiate an instance of a service provider
+     * defined in the META-INF/services directory of tracked bundles.
+     *
+     * @param providerId The name of the target interface class.
+     *
+     * @return The service instance.  Returns null if no service defintions
+     *         can be located.
+     * @exception Exception Any classloading or other exceptions thrown during
+     *                      the process of creating this service instance.
+     */
+    public Object getService(String providerId) throws Exception;
+
+    /**
+     * Locate all services that match a given provider id and create instances.
+     *
+     * @param providerId The target provider identifier.
+     *
+     * @return A List containing the instances corresponding to the
+     *         provider identifier.  Returns an empty list if no
+     *         matching classes can be located or created
+     */
+    public List<Object> getServices(String providerId);
+
+
+    /**
+     * Locate and return the class for a service provider
+     * defined in the META-INF/services directory of tracked bundles.
+     *
+     * @param providerId The name of the target interface class.
+     *
+     * @return The provider class.   Returns null if no service defintions
+     *         can be located.
+     * @exception Exception Any classloading or other exceptions thrown during
+     *                      the process of loading this service provider class.
+     */
+    public Class<?> getServiceClass(String providerId) throws ClassNotFoundException;
+
+
+    /**
+     * Locate all services that match a given provider id and return the implementation
+     * classes
+     *
+     * @param providerId The target provider identifier.
+     *
+     * @return A List containing the classes corresponding to the
+     *         provider identifier.  Returns an empty list if no
+     *         matching classes can be located.
+     */
+    public List<Class<?>> getServiceClasses(String providerId);
+
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/BundleProviderLoader.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/BundleProviderLoader.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/BundleProviderLoader.java
new file mode 100644
index 0000000..20b7c47
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/BundleProviderLoader.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.yoko.osgi.locator;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Holder class for located services information.
+ */
+public class BundleProviderLoader implements Comparable<BundleProviderLoader> {
+    // the class name for this provider
+    private final String providerId;
+    // the mapped class name of the provider.
+    private final String providerClass;
+    // the hosting bundle.
+    private final Bundle bundle;
+
+    private final int priority;
+
+    /**
+     * Create a loader for this registered provider.
+     *
+     * @param providerId The provider ID
+     * @param providerClass The mapped class name of the provider.
+     * @param bundle    The hosting bundle.
+     * @param priority
+     */
+    public BundleProviderLoader(String providerId, String providerClass, Bundle bundle, int priority) {
+        this.providerId = providerId;
+        this.providerClass = providerClass;
+        this.bundle = bundle;
+        this.priority = priority;
+    }
+
+    /**
+     * Load a provider class.
+     *
+     * @return The provider class from the target bundle.
+     * @exception Exception
+     */
+    public Class<?> loadClass() throws ClassNotFoundException {
+        try {
+//                log(LogService.LOG_DEBUG, "loading class for: " + this);
+            return bundle.loadClass(providerClass);
+        } catch (ClassNotFoundException e) {
+//                log(LogService.LOG_DEBUG, "exception caught while loading " + this, e);
+            throw e;
+        }
+    }
+
+    /**
+     * Create an instance of the registred service.
+     *
+     * @return The created instance.  A new instance is created on each call.
+     * @exception Exception
+     */
+    public Object createInstance() throws Exception {
+        // get the class object
+        Class <?> cls = loadClass();
+        try {
+            // just create an instance using the default constructor
+            return cls.newInstance();
+        } catch (Exception e) {
+//                log(LogService.LOG_DEBUG, "exception caught while creating " + this, e);
+            throw e;
+        } catch (Error e) {
+//                log(LogService.LOG_DEBUG, "error caught while creating " + this, e);
+            throw e;
+        }
+    }
+
+
+    public String id() {
+        return providerId;
+    }
+
+    @Override
+    public String toString() {
+        return "Provider interface=" + providerId + " , provider class=" + providerClass + ", bundle=" + bundle;
+    }
+
+    @Override
+    public int hashCode() {
+        return providerId.hashCode() + providerClass.hashCode() + (int)bundle.getBundleId();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof BundleProviderLoader) {
+            return providerId.equals(((BundleProviderLoader)obj).providerId) &&
+                   providerClass.equals(((BundleProviderLoader)obj).providerClass) &&
+                   bundle.getBundleId() == ((BundleProviderLoader)obj).bundle.getBundleId();
+        } else {
+            return false;
+        }
+    }
+
+    public int compareTo(BundleProviderLoader other) {
+        return other.priority - priority;
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderBean.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderBean.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderBean.java
new file mode 100644
index 0000000..688ff61
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderBean.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.yoko.osgi.locator;
+
+import java.util.logging.Logger;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ProviderBean {
+    private static final Logger log = Logger.getLogger(ProviderBean.class.getName());
+
+    private Register providerRegistry;
+
+    private BundleProviderLoader bundleProviderLoader;
+
+    public ProviderBean(String key,
+                       String className,
+                       Bundle bundle,
+                       Register providerRegistry) {
+        this(key, className, bundle, providerRegistry, -1);
+    }
+
+    public ProviderBean(String key,
+                        String className,
+                        Bundle bundle,
+                        Register providerRegistry,
+                        Integer priority) {
+        bundleProviderLoader = new BundleProviderLoader(key, className, bundle, priority == null? -1: priority);
+        log.finer("ProviderBean: " + bundleProviderLoader);
+        this.providerRegistry = providerRegistry;
+    }
+
+
+
+    public void start() {
+        providerRegistry.registerProvider(bundleProviderLoader);
+    }
+
+    public void stop() {
+        providerRegistry.unregisterProvider(bundleProviderLoader);
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderRegistryImpl.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderRegistryImpl.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderRegistryImpl.java
new file mode 100644
index 0000000..31dfa31
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ProviderRegistryImpl.java
@@ -0,0 +1,327 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.yoko.osgi.locator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.yoko.osgi.ProviderLocator;
+import org.apache.yoko.osgi.ProviderRegistry;
+
+/**
+ * The implementation of the provider registry used to store
+ * the bundle registrations.
+ */
+public class ProviderRegistryImpl implements ProviderRegistry, Register {
+
+    private static final Logger log = Logger.getLogger(ProviderRegistryImpl.class.getName());
+    // our mapping between a provider id and the implementation information.  There
+    // might be a one-to-many relationship between the ids and implementing classes.
+    private SPIRegistry providers = new SPIRegistry();
+    // our mapping between an interface name and a META-INF/services SPI implementation.  There
+    // might be a one-to-many relationship between the ids and implementing classes.
+    private SPIRegistry serviceProviders = new SPIRegistry();
+
+    public void start() {
+        ProviderLocator.setRegistry(this);
+    }
+
+    public void stop() {
+        ProviderLocator.setRegistry(null);
+    }
+
+    /**
+     * Register an individual provivider item by its provider identifier.
+     *
+     * @param provider The loader used to resolve the provider class.
+     */
+    public void registerProvider(BundleProviderLoader provider) {
+        log(Level.FINE, "registering provider " + provider);
+        providers.register(provider);
+    }
+
+    /**
+     * Removed a provider registration for a named provider id.
+     *
+     * @param provider The provider registration instance
+     */
+    public void unregisterProvider(BundleProviderLoader provider) {
+        log(Level.FINE, "unregistering provider " + provider);
+        providers.unregister(provider);
+    }
+
+
+    /**
+     * Register an individual provivider item by its provider identifier.
+     *
+     * @param provider The loader used to resolve the provider class.
+     */
+    public void registerService(BundleProviderLoader provider) {
+        log(Level.FINE, "registering service " + provider);
+        serviceProviders.register(provider);
+    }
+
+    /**
+     * Removed a provider registration for a named provider id.
+     *
+     * @param provider The provider registration instance
+     */
+    public void unregisterService(BundleProviderLoader provider) {
+        log(Level.FINE, "unregistering service " + provider);
+        serviceProviders.unregister(provider);
+    }
+
+    /**
+     * Locate a class by its provider id indicator. .
+     *
+     * @param providerId The provider id (generally, a fully qualified class name).
+     *
+     * @return The Class corresponding to this provider id.  Returns null
+     *         if this is not registered or the indicated class can't be
+     *         loaded.
+     */
+    public Class<?> locate(String providerId) {
+        // see if we have a registered match for this...getting just the first instance
+        BundleProviderLoader loader = providers.getLoader(providerId);
+        if (loader != null) {
+            try {
+                // try to load this.  We always return null
+                return loader.loadClass();
+            } catch (Exception e) {
+                e.printStackTrace();
+                // just swallow this and return null.  The exception has already
+                // been logged.
+            }
+        }
+        // no match to return
+        return null;
+    }
+
+    /**
+     * Locate all class files that match a given provider id.
+     *
+     * @param providerId The target provider identifier.
+     *
+     * @return A List containing the class objects corresponding to the
+     *         provider identifier.  Returns an empty list if no
+     *         matching classes can be located.
+     */
+    public List<Class<?>> locateAll(String providerId) {
+        List<Class<?>> classes = new ArrayList<Class<?>>();
+        List<BundleProviderLoader> l = providers.getLoaders(providerId);
+        // this returns null if nothing is found.
+        if (l != null) {
+            for (BundleProviderLoader c : l) {
+                try {
+                    classes.add(c.loadClass());
+                } catch (Exception e) {
+                    // just swallow this and proceed to the next.  The exception has
+                    // already been logged.
+                }
+            }
+        }
+        return classes;
+    }
+
+    /**
+     * Locate and instantiate an instance of a service provider
+     * defined in the META-INF/services directory of tracked bundles.
+     *
+     * @param providerId The name of the target interface class.
+     *
+     * @return The service instance.  Returns null if no service defintions
+     *         can be located.
+     * @exception Exception Any classloading or other exceptions thrown during
+     *                      the process of creating this service instance.
+     */
+    public Object getService(String providerId) throws Exception {
+        // see if we have a registered match for this...getting just the first instance
+        BundleProviderLoader loader = serviceProviders.getLoader(providerId);
+        if (loader != null) {
+            // try to load this and create an instance.  Any/all exceptions get
+            // thrown here
+            return loader.createInstance();
+        }
+        // no match to return
+        return null;
+    }
+
+    /**
+     * Locate all services that match a given provider id and create instances.
+     *
+     * @param providerId The target provider identifier.
+     *
+     * @return A List containing the instances corresponding to the
+     *         provider identifier.  Returns an empty list if no
+     *         matching classes can be located or created
+     */
+    public List<Object> getServices(String providerId) {
+        List<Object> instances = new ArrayList<Object>();
+        List<BundleProviderLoader> l = serviceProviders.getLoaders(providerId);
+        // this returns null for nothing found
+        if (l != null) {
+            for (BundleProviderLoader c : l) {
+                try {
+                    instances.add(c.createInstance());
+                } catch (Exception e) {
+                    // just swallow this and proceed to the next.  The exception has
+                    // already been logged.
+                }
+            }
+        }
+        return instances;
+    }
+
+    /**
+     * Locate all services that match a given provider id and return the implementation
+     * classes
+     *
+     * @param providerId The target provider identifier.
+     *
+     * @return A List containing the classes corresponding to the
+     *         provider identifier.  Returns an empty list if no
+     *         matching classes can be located.
+     */
+    public List<Class<?>> getServiceClasses(String providerId) {
+        List<Class<?>> classes = new ArrayList<Class<?>>();
+        List<BundleProviderLoader> l = serviceProviders.getLoaders(providerId);
+        // this returns null for nothing found
+        if (l != null) {
+            for (BundleProviderLoader c : l) {
+                try {
+                    classes.add(c.loadClass());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    // just swallow this and proceed to the next.  The exception has
+                    // already been logged.
+                }
+            }
+        }
+        return classes;
+    }
+
+    /**
+     * Locate and return the class for a service provider
+     * defined in the META-INF/services directory of tracked bundles.
+     *
+     * @param providerId The name of the target interface class.
+     *
+     * @return The provider class.   Returns null if no service defintions
+     *         can be located.
+     * @exception ClassNotFoundException Any classloading or other exceptions thrown during
+     *                      the process of loading this service provider class.
+     */
+    public Class<?> getServiceClass(String providerId) throws ClassNotFoundException {
+        // see if we have a registered match for this...getting just the first instance
+        BundleProviderLoader loader = serviceProviders.getLoader(providerId);
+        if (loader != null) {
+            // try to load this and create an instance.  Any/all exceptions get
+            // thrown here
+            return loader.loadClass();
+        }
+        // no match to return
+        return null;
+    }
+
+    private void log(Level level, String message) {
+        log.log(level, message);
+    }
+
+    /**
+     * Holder class for information about a given collection of
+     * id to provider mappings.  Used for both the providers and
+     * the services.
+     */
+    private class SPIRegistry {
+        private Map<String, List<BundleProviderLoader>> registry;
+
+
+        /**
+         * Register an individual provivider item by its provider identifier.
+         *
+         * @param provider The loader used to resolve the provider class.
+         */
+        public synchronized void register(BundleProviderLoader provider) {
+            // if this is the first registration, create the mapping table
+            if (registry == null) {
+                registry = new HashMap<String, List<BundleProviderLoader>>();
+            }
+
+            String providerId = provider.id();
+
+            // the providers are stored as a list...we use the first one registered
+            // when asked to locate.
+            List<BundleProviderLoader> l = registry.get(providerId);
+            if (l ==  null) {
+                l = new ArrayList<BundleProviderLoader>(2);
+                registry.put(providerId, l);
+            }
+            l.add(provider);
+            Collections.sort(l);
+        }
+
+        /**
+         * Remove a provider registration for a named provider id.
+         *
+         * @param provider The provider registration instance
+         */
+        public synchronized void unregister(BundleProviderLoader provider) {
+            if (registry != null) {
+                // this is stored as a list.  Just remove using the registration information
+                // This may move a different provider to the front of the list.
+                List<BundleProviderLoader> l = registry.get(provider.id());
+                if (l != null) {
+                    l.remove(provider);
+                }
+            }
+        }
+
+        private synchronized BundleProviderLoader getLoader(String id) {
+            // synchronize on the registry instance
+            if (registry != null) {
+                log.fine("registry: " + registry);
+                // return the first match, if any
+                List<BundleProviderLoader> list = registry.get(id);
+                if (list != null && !list.isEmpty()) {
+                    return list.get(0);
+                }
+            }
+            // no match here
+            return null;
+        }
+
+        private synchronized List<BundleProviderLoader> getLoaders(String id) {
+            if (registry != null) {
+                // if we have matches, return a copy of what we currently have
+                // to create a safe local copy.
+                List<BundleProviderLoader> list = registry.get(id);
+                if (list != null && !list.isEmpty()) {
+                    return new ArrayList<BundleProviderLoader>(list);
+                }
+            }
+            // no match here
+            return null;
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/Register.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/Register.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/Register.java
new file mode 100644
index 0000000..030b390
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/Register.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.yoko.osgi.locator;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface Register {
+
+    void registerProvider(BundleProviderLoader provider);
+
+    void unregisterProvider(BundleProviderLoader provider);
+
+    void registerService(BundleProviderLoader provider);
+
+    void unregisterService(BundleProviderLoader provider);
+
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ServiceBean.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ServiceBean.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ServiceBean.java
new file mode 100644
index 0000000..7876495
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/ServiceBean.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.yoko.osgi.locator;
+
+import java.util.logging.Logger;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ServiceBean {
+    private static final Logger log = Logger.getLogger(ServiceBean.class.getName());
+
+    private Register providerRegistry;
+
+    private BundleProviderLoader bundleProviderLoader;
+
+    public ServiceBean(String key,
+                       String className,
+                       Bundle bundle,
+                       Register providerRegistry) {
+        this(key, className, bundle, providerRegistry, -1);
+    }
+    
+    public ServiceBean(String key,
+                       String className,
+                       Bundle bundle,
+                       Register providerRegistry,
+                       Integer priority) {
+        bundleProviderLoader = new BundleProviderLoader(key, className, bundle, priority == null? -1: priority);
+        log.finer("ServiceBean: " + bundleProviderLoader);
+        this.providerRegistry = providerRegistry;
+    }
+
+
+
+    public void start() {
+        providerRegistry.registerService(bundleProviderLoader);
+    }
+
+    public void stop() {
+        providerRegistry.unregisterService(bundleProviderLoader);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/activator/AbstractBundleActivator.java
----------------------------------------------------------------------
diff --git a/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/activator/AbstractBundleActivator.java b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/activator/AbstractBundleActivator.java
new file mode 100644
index 0000000..c21cf42
--- /dev/null
+++ b/yoko-osgi/src/main/java/org/apache/yoko/osgi/locator/activator/AbstractBundleActivator.java
@@ -0,0 +1,107 @@
+package org.apache.yoko.osgi.locator.activator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.yoko.osgi.locator.BundleProviderLoader;
+import org.apache.yoko.osgi.locator.Register;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractBundleActivator implements BundleActivator {
+
+	public static class Info {
+	    final String id;
+	    final String className;
+        final int priority;
+        
+		public Info(String id, String className, int priority) {
+			super();
+			this.id = id;
+			this.className = className;
+			this.priority = priority;
+		}
+		
+	}
+	private final Info[] providerInfo;
+    private final Info[] serviceInfo;
+	private ServiceTracker<Register, Register> tracker;
+	private BundleContext context;
+	private boolean registered;
+	private final List<BundleProviderLoader> providerLoaders = new ArrayList<BundleProviderLoader>();
+	private final List<BundleProviderLoader> serviceLoaders = new ArrayList<BundleProviderLoader>();
+	
+	public AbstractBundleActivator(Info[] providerInfo, Info[] serviceInfo) {
+		this.providerInfo = providerInfo;
+		this.serviceInfo = serviceInfo;
+	}
+
+	public void start(final BundleContext context) throws Exception {
+		this.context = context;
+		tracker = new ServiceTracker<Register, Register>(context, Register.class, new ServiceTrackerCustomizer<Register, Register>() {
+
+			public Register addingService(ServiceReference<Register> reference) {
+				Register register = context.getService(reference);
+				register(register);
+				return register;
+			}
+
+			public void modifiedService(ServiceReference<Register> reference,
+					Register service) {
+				// TODO Auto-generated method stub
+				
+			}
+
+			public void removedService(ServiceReference<Register> reference,
+					Register service) {
+				// TODO Auto-generated method stub
+				
+			}
+			
+		});
+		tracker.open();
+		Register register = tracker.getService();
+		if (register != null) {
+			register(register);
+		}
+
+	}
+
+	private synchronized void register(Register register) {
+		if (!registered) {
+			registered = true;
+			Bundle bundle = context.getBundle();
+			for (Info classInfo: providerInfo) {
+				BundleProviderLoader loader = new BundleProviderLoader(classInfo.id, classInfo.className, bundle, classInfo.priority);
+				providerLoaders.add(loader);
+				register.registerProvider(loader);
+			}
+			for (Info classInfo: serviceInfo) {
+				BundleProviderLoader loader = new BundleProviderLoader(classInfo.id, classInfo.className, bundle, classInfo.priority);
+				serviceLoaders.add(loader);
+				register.registerService(loader);
+			}
+		}
+	}
+
+	public void stop(BundleContext context) throws Exception {
+		Register register = tracker.getService();
+		tracker.close();
+		synchronized (this) {
+			if (register != null && registered) {
+				for (BundleProviderLoader loader: providerLoaders) {
+					register.unregisterProvider(loader);
+				}
+				for (BundleProviderLoader loader: serviceLoaders) {
+					register.unregisterService(loader);
+				}
+			}
+		}
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-rmi-impl/pom.xml
----------------------------------------------------------------------
diff --git a/yoko-rmi-impl/pom.xml b/yoko-rmi-impl/pom.xml
index a436390..7b8d098 100755
--- a/yoko-rmi-impl/pom.xml
+++ b/yoko-rmi-impl/pom.xml
@@ -32,6 +32,11 @@
     an endorsed standard -->
         <dependency>
             <groupId>org.apache.yoko</groupId>
+            <artifactId>yoko-osgi</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.yoko</groupId>
             <artifactId>yoko-spec-corba</artifactId>
             <scope>provided</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/api/PortableRemoteObjectExt.java
----------------------------------------------------------------------
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/api/PortableRemoteObjectExt.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/api/PortableRemoteObjectExt.java
index 94a5aa5..6d0e16e 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/api/PortableRemoteObjectExt.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/api/PortableRemoteObjectExt.java
@@ -21,7 +21,7 @@ package org.apache.yoko.rmi.api;
 import java.security.AccessController;
 
 import org.apache.yoko.rmi.util.GetSystemPropertyAction;
-import org.apache.yoko.util.osgi.ProviderLocator;
+import org.apache.yoko.osgi.ProviderLocator;
 
 public class PortableRemoteObjectExt {
 

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/UtilImpl.java
----------------------------------------------------------------------
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/UtilImpl.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/UtilImpl.java
index 55ba438..2131bb7 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/UtilImpl.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/UtilImpl.java
@@ -47,7 +47,7 @@ import javax.rmi.CORBA.UtilDelegate;
 import javax.rmi.CORBA.ValueHandler;
 
 import org.apache.yoko.rmi.util.GetSystemPropertyAction;
-import org.apache.yoko.util.osgi.ProviderLocator;
+import org.apache.yoko.osgi.ProviderLocator;
 import org.omg.CORBA.Any;
 import org.omg.CORBA.BAD_PARAM;
 import org.omg.CORBA.COMM_FAILURE;

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/osgi/activator/Activator.java
----------------------------------------------------------------------
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/osgi/activator/Activator.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/osgi/activator/Activator.java
index 6c37409..573112f 100644
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/osgi/activator/Activator.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/osgi/activator/Activator.java
@@ -2,9 +2,9 @@ package org.apache.yoko.rmi.osgi.activator;
 
 import javax.rmi.CORBA.Stub;
 
-import org.apache.yoko.util.osgi.locator.ProviderRegistryImpl;
-import org.apache.yoko.util.osgi.locator.Register;
-import org.apache.yoko.util.osgi.locator.activator.AbstractBundleActivator;
+import org.apache.yoko.osgi.locator.ProviderRegistryImpl;
+import org.apache.yoko.osgi.locator.Register;
+import org.apache.yoko.osgi.locator.activator.AbstractBundleActivator;
 import org.omg.stub.java.rmi._Remote_Stub;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-rmi-spec/pom.xml
----------------------------------------------------------------------
diff --git a/yoko-rmi-spec/pom.xml b/yoko-rmi-spec/pom.xml
index 3ac0cea..4506f7b 100644
--- a/yoko-rmi-spec/pom.xml
+++ b/yoko-rmi-spec/pom.xml
@@ -35,6 +35,10 @@
             <artifactId>yoko-spec-corba</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.yoko</groupId>
+            <artifactId>yoko-osgi</artifactId>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-rmi-spec/src/main/java/org/apache/yoko/rmispec/util/UtilLoader.java
----------------------------------------------------------------------
diff --git a/yoko-rmi-spec/src/main/java/org/apache/yoko/rmispec/util/UtilLoader.java b/yoko-rmi-spec/src/main/java/org/apache/yoko/rmispec/util/UtilLoader.java
index 54f2da9..cbcd57d 100644
--- a/yoko-rmi-spec/src/main/java/org/apache/yoko/rmispec/util/UtilLoader.java
+++ b/yoko-rmi-spec/src/main/java/org/apache/yoko/rmispec/util/UtilLoader.java
@@ -18,7 +18,7 @@
 
 package org.apache.yoko.rmispec.util;
 
-import org.apache.yoko.util.osgi.ProviderLocator;
+import org.apache.yoko.osgi.ProviderLocator;
 
 import java.net.MalformedURLException;
 import java.net.URL;

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-spec-corba/pom.xml
----------------------------------------------------------------------
diff --git a/yoko-spec-corba/pom.xml b/yoko-spec-corba/pom.xml
index 3d95e9b..cb2f75d 100755
--- a/yoko-spec-corba/pom.xml
+++ b/yoko-spec-corba/pom.xml
@@ -31,7 +31,7 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.yoko</groupId>
-            <artifactId>yoko-util</artifactId>
+            <artifactId>yoko-osgi</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-spec-corba/src/main/java/org/omg/CORBA/ORB.java
----------------------------------------------------------------------
diff --git a/yoko-spec-corba/src/main/java/org/omg/CORBA/ORB.java b/yoko-spec-corba/src/main/java/org/omg/CORBA/ORB.java
index 2063a27..b880190 100755
--- a/yoko-spec-corba/src/main/java/org/omg/CORBA/ORB.java
+++ b/yoko-spec-corba/src/main/java/org/omg/CORBA/ORB.java
@@ -16,7 +16,7 @@
  */
 package org.omg.CORBA;
 
-import org.apache.yoko.util.osgi.ProviderLocator;
+import org.apache.yoko.osgi.ProviderLocator;
 
 import java.security.PrivilegedAction;
 import java.security.AccessController;

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPoint.java
----------------------------------------------------------------------
diff --git a/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPoint.java b/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPoint.java
index e76566a..157173a 100755
--- a/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPoint.java
+++ b/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPoint.java
@@ -26,19 +26,11 @@ final public class ListenPoint implements org.omg.CORBA.portable.IDLEntity
 {
     private static final String _ob_id = "IDL:omg.org/IIOP/ListenPoint:1.0";
 
-    public
-    ListenPoint()
-    {
-    }
-
-    public
-    ListenPoint(String host,
-                short port)
-    {
+    public ListenPoint(String host, short port) {
         this.host = host;
         this.port = port;
     }
 
-    public String host;
-    public short port;
+    public final String host;
+    public final short port;
 }

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPointHelper.java
----------------------------------------------------------------------
diff --git a/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPointHelper.java b/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPointHelper.java
index bc679da..5e7d62c 100755
--- a/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPointHelper.java
+++ b/yoko-spec-corba/src/main/java/org/omg/IIOP/ListenPointHelper.java
@@ -72,10 +72,7 @@ final public class ListenPointHelper
     public static ListenPoint
     read(org.omg.CORBA.portable.InputStream in)
     {
-        ListenPoint _ob_v = new ListenPoint();
-        _ob_v.host = in.read_string();
-        _ob_v.port = in.read_ushort();
-        return _ob_v;
+        return new ListenPoint(in.read_string(), in.read_ushort());
     }
 
     public static void

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/pom.xml
----------------------------------------------------------------------
diff --git a/yoko-util/pom.xml b/yoko-util/pom.xml
index 9bdabfa..2c6c625 100644
--- a/yoko-util/pom.xml
+++ b/yoko-util/pom.xml
@@ -26,24 +26,33 @@
 
     <artifactId>yoko-util</artifactId>
 
-    <name>Apache Yoko Utilities</name>
+    <name>Apache Yoko Implementation Utilities</name>
 
     <packaging>bundle</packaging>
 
     <dependencies>
         <!-- this contains the osgi-relevant classes in the endorsed dir -->
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <version>5.0.0</version>
+            <groupId>org.apache.yoko</groupId>
+            <artifactId>yoko-spec-corba</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.compendium</artifactId>
-            <version>5.0.0</version>
+            <groupId>org.apache.yoko</groupId>
+            <artifactId>yoko-rmi-spec</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>1.9.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -59,6 +68,15 @@
                     </instructions>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <includes>
+                        <include>**/*Test.java</include>
+                    </includes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/Cache.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/Cache.java b/yoko-util/src/main/java/org/apache/yoko/util/Cache.java
new file mode 100644
index 0000000..8a4f3d9
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/Cache.java
@@ -0,0 +1,47 @@
+package org.apache.yoko.util;
+
+import java.util.Map;
+
+public interface Cache<K, V> {
+    /** Get the number of cached values */
+    int size();
+
+    /** Get the number of cached values not currently in use */
+    int idleCount();
+
+    /**
+     * Retrieve the value for the given key.
+     * The caller must ensure the returned reference is closed
+     */
+    Reference<V> get(K key);
+
+    /**
+     * Retrieve or compute the value for the given key.
+     * The caller must ensure that the returned reference is closed.
+     * @return an auto-closeable reference to the cached value
+     */
+    Reference<V> getOrCreate(K key, KeyedFactory<K, V> keyedFactory);
+
+    /**
+     * Retrieve or compute the value for the given key.
+     * The caller must ensure that the returned reference is closed.
+     * @return an auto-closeable reference to the cached value
+     */
+    Reference<V> getOrCreate(K key, Factory<V> factory);
+
+    /**
+     * Uncache an item. No cleanup will be performed.
+     * @throws IllegalStateException if valueRef has already been closed
+     */
+    void remove(Reference<V> ref);
+
+    /**
+     * Remove some idle entries.
+     * @return the number of entries removed
+     */
+    int clean();
+
+    Map<K, V> snapshot();
+
+    interface Cleaner<V> {void clean(V value);}
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/Factory.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/Factory.java b/yoko-util/src/main/java/org/apache/yoko/util/Factory.java
new file mode 100644
index 0000000..0c86537
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/Factory.java
@@ -0,0 +1,5 @@
+package org.apache.yoko.util;
+
+public interface Factory<V> {
+    V create();
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/Fifa.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/Fifa.java b/yoko-util/src/main/java/org/apache/yoko/util/Fifa.java
new file mode 100644
index 0000000..403c397
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/Fifa.java
@@ -0,0 +1,6 @@
+package org.apache.yoko.util;
+
+/** A first-in, first-accessed holder of stuff */
+public interface Fifa<T> extends Sequential<T> {
+    T peek();
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/Fifo.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/Fifo.java b/yoko-util/src/main/java/org/apache/yoko/util/Fifo.java
new file mode 100644
index 0000000..e85376e
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/Fifo.java
@@ -0,0 +1,6 @@
+package org.apache.yoko.util;
+
+/** A first-in, first-out holder of stuff */
+public interface Fifo<T> extends Sequential<T> {
+    Object remove();
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/KeyedFactory.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/KeyedFactory.java b/yoko-util/src/main/java/org/apache/yoko/util/KeyedFactory.java
new file mode 100644
index 0000000..4212dd7
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/KeyedFactory.java
@@ -0,0 +1,5 @@
+package org.apache.yoko.util;
+
+public interface KeyedFactory<K, V> {
+    V create(K key);
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/Reference.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/Reference.java b/yoko-util/src/main/java/org/apache/yoko/util/Reference.java
new file mode 100644
index 0000000..5d15034
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/Reference.java
@@ -0,0 +1,9 @@
+package org.apache.yoko.util;
+
+public interface Reference<T> extends AutoCloseable {
+    /** Get the referent, which is guaranteed to be non-null */
+    T get();
+    /** Finish using the reference */
+    @Override
+    void close();
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/Sequential.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/Sequential.java b/yoko-util/src/main/java/org/apache/yoko/util/Sequential.java
new file mode 100644
index 0000000..ad1cd20
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/Sequential.java
@@ -0,0 +1,18 @@
+package org.apache.yoko.util;
+
+/** A holder of stuff */
+public interface Sequential<T> {
+    int size();
+
+    Place<T> put(T elem);
+
+    interface Place<T> {
+        /**
+         * Relinquish this place in the sequence.
+         *
+         * @return the element if it is successfully removed from the sequence<br>
+         * or <code>null</code> if the element has already been removed by another operation
+         */
+        T relinquish();
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/concurrent/ConcurrentFifo.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/ConcurrentFifo.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/ConcurrentFifo.java
new file mode 100644
index 0000000..67d2eb3
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/ConcurrentFifo.java
@@ -0,0 +1,164 @@
+package org.apache.yoko.util.concurrent;
+
+import org.apache.yoko.util.Fifa;
+import org.apache.yoko.util.Fifo;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A thread-safe queue that allows concurrent modification of non-adjacent elements.
+ */
+class ConcurrentFifo<T> implements Fifo<T>, Fifa<T> {
+    /*
+     * This class relies on consistent lock ordering. Locks are ALWAYS obtained
+     * in the order of elements in the queue. The locks used are the monitors
+     * of the node elements, and each node's monitor guards the relationship
+     * between that node and its successor. By implication, it guards the other
+     * node's back reference as well.
+     *
+     * So, for a delete operation, two nodes must be locked: the node to be
+     * deleted, and the previous node, but NOT IN THAT ORDER! Moreover, after
+     * the previous node is locked, the relationship must be checked to ensure
+     * it is still current. The convention observed is that next() is only
+     * accessed while the lock is held, and it is cross-checked against any
+     * unguarded calls to prev().
+     *
+     * NOTE: this is not double-check locking (DCL) because the early access
+     * never obviates a synchronized block, and results are always checked 
+     * within the guarded section. Therefore, it is not necessary for any of
+     * the non-final fields to be volatile.
+     * 
+     * DCL: https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl
+     *
+     * If inconsistency is detected, all locks are to be released and the
+     * operation restarted from scratch, unless it can be determined the
+     * operation has definitively failed due to concurrent modification.
+     *
+     * Operations on non-adjacent nodes are concurrent. Concurrent operations
+     * on a node or on adjacent nodes will contend for monitors, but never
+     * deadlock.
+     */
+
+    private final Head<T> head = new Head<>();
+    private final Foot<T> foot = new Foot<>(head);
+    protected final AtomicInteger size = new AtomicInteger(0);
+
+    @Override
+    public int size() { return size.get(); }
+
+    /**
+     * Get, without removing it, the oldest remaining element from this FIFO.
+     * This method does not block and returns an answer consistent with the state of the FIFO at some point.
+     *
+     * @return the oldest remaining element or <code>null</code> if the FIFO was empty
+     */
+    @Override
+    public T peek() {
+        return recursivePeek(head);
+    }
+
+    /**
+     * Find the first non-null value.
+     */
+    private T recursivePeek(PNode<T> start) {
+        synchronized (start) {
+            NNode<T> nn = start.next();
+            if (nn == foot) return null;
+            VNode<T> node = (VNode<T>) nn;
+            T result = node.get();
+            return (result == null) ? recursivePeek(node) : result;
+        }
+    }
+
+    /**
+     * Add an element to the end of this FIFO.
+     *
+     * @param elem must not be <code>null</code>
+     * @return an object representing the place in the queue
+     */
+    @Override
+    public Place<T> put(T elem) {
+        do {
+            final PNode<T> pnode = foot.prev();
+            // lock penultimate node
+            synchronized (pnode) {
+                // RETRY if structure changed
+                if (pnode.next() != foot) continue;
+                // create a new node
+                final VNode<T> node = createNode(elem);
+                // insert new node
+                synchronized (node) {
+                    node.insertAfter(pnode);
+                    size.incrementAndGet();
+                }
+                // return place in queue
+                return new Place<T>() {
+                    @Override
+                    public T relinquish() {
+                        return remove(node);
+                    }
+                };
+            }
+        } while (true);
+    }
+
+    protected VNode<T> createNode(T elem) {
+        return new StrongNode<>(requireNonNull(elem));
+    }
+
+    /**
+     * Remove the least recently added element.
+     *
+     * @return the removed element, or <code>null</code> if the FIFO was empty.
+     */
+    @Override
+    public T remove() {
+        return recursiveRemove(head);
+    }
+
+    /**
+     * Find and remove the first non-null value
+     */
+    private T recursiveRemove(PNode<T> start) {
+        synchronized (start) {
+            NNode<T> nn = start.next();
+            if (nn == foot) return null;
+            VNode<T> node = (VNode<T>) nn;
+            T result = node.get();
+            if (result == null)
+                return recursiveRemove(node);
+            synchronized (node) {
+                node.delete();
+                size.decrementAndGet();
+                return result;
+            }
+        }
+    }
+
+    /**
+     * Remove the specified node from the FIFO, if present.
+     * @return the element if it was successfully removed,
+     *         otherwise <code>null</code>
+     */
+    protected T remove(VNode<T> node) {
+        do {
+            // retrieve previous node
+            final PNode<T> pNode = node.prev();
+            // FAIL if node already deleted
+            if (pNode == null) return null;
+            // lock previous node
+            synchronized (pNode) {
+                // RETRY if structure has changed
+                if (pNode.next() != node) continue;
+                // Remove node from chain and report success
+                synchronized (node) {
+                    node.delete();
+                    size.decrementAndGet();
+                }
+                return node.get();
+            }
+        } while (true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/concurrent/CountedEntry.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/CountedEntry.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/CountedEntry.java
new file mode 100644
index 0000000..0177f5f
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/CountedEntry.java
@@ -0,0 +1,153 @@
+package org.apache.yoko.util.concurrent;
+
+import org.apache.yoko.util.Reference;
+import org.apache.yoko.util.Sequential;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A thread-safe, reference-counting entry for use in a cache.
+ * If threads race to call @link{#clear} and @link{#obtain},
+ * one or other method will return <code>null</code>.
+ * <br>
+ * Entries with a reference count of zero will be put onto the
+ * provided @link{Sequential} object, and removed on successful
+ * calls to either @link{#clear} or @link{#obtain}.
+ */
+class CountedEntry<K, V> {
+    private static final int CLEANED = Integer.MIN_VALUE;
+    private static final int NOT_READY = -1;
+    private static final int IDLE = -2;
+    private final AtomicInteger refCount = new AtomicInteger(NOT_READY);
+    private final Sequential<CountedEntry<K, V>> idleEntries;
+    private Sequential.Place<?> idlePlace;
+    private V value;
+    final K key;
+
+    /** Create a not-yet-ready CountedEntry - the next operation must be to call setValue() or abort() */
+    CountedEntry(K key, Sequential<CountedEntry<K, V>> idleEntries) {
+        this.key = key;
+        this.idleEntries = idleEntries;
+    }
+
+    ValueReference setValue(V value) {
+        this.value = Objects.requireNonNull(value);
+        notifyReady(1);
+        return new ValueReference();
+    }
+
+    void abort() {
+        assert value == null;
+        notifyReady(CLEANED);
+    }
+
+    private synchronized void notifyReady(int newCount) {
+        boolean success = refCount.compareAndSet(NOT_READY, newCount);
+        assert success;
+        this.notifyAll();
+    }
+
+    private synchronized void blockWhileNotReady() {
+        while (refCount.get() == NOT_READY) {
+            try {
+                this.wait();
+            } catch (InterruptedException ignored) {
+            }
+        }
+    }
+
+    // Acquire a reference to this entry.
+    private boolean acquire() {
+        RESPIN: do {
+            int oldCount = refCount.get();
+            switch (oldCount) {
+                case CLEANED:
+                    // terminal state - must fail
+                    return false;
+                case NOT_READY:
+                    blockWhileNotReady();
+                    continue RESPIN;
+                case IDLE:
+                    // grab the ref while it's idle or start again
+                    if (!!!refCount.compareAndSet(IDLE, NOT_READY)) continue RESPIN;
+                    // remove from the idle list
+                    Object self = idlePlace.relinquish();
+                    assert this == self;
+                    idlePlace = null;
+                    // let other threads know this entry is accessible again
+                    notifyReady(1);
+                    return true;
+                default:
+                    // increment the value retrieved or start again
+                    if (!!!refCount.compareAndSet(oldCount, oldCount + 1)) continue RESPIN;
+                    return true;
+            }
+        } while (true);
+    }
+
+    // Release a reference to this entry. Only the owner of the reference should call this method.
+    private boolean release() {
+        int newCount = refCount.decrementAndGet();
+        if (newCount != 0) return true;
+
+        // try to IDLE this entry
+        if (!!!refCount.compareAndSet(0, NOT_READY))
+            // some other thread revived or purged this entry, so no need to IDLE it now
+            return true;
+
+        idlePlace = idleEntries.put(this);
+        notifyReady(IDLE);
+        return true;
+    }
+
+    // Mark this entry unusable. Return value if entry is modified, null otherwise.
+    V clear() {
+        if (!!! refCount.compareAndSet(IDLE, CLEANED)) return null;
+        // safe to read/update idlePlace since this is the only thread that has moved it from IDLE
+        try {
+            Object self = idlePlace.relinquish();
+            assert self == this;
+            return value;
+        } finally {
+            value = null;
+            idlePlace = null;
+        }
+    }
+
+    ValueReference obtain() {return acquire() ? new ValueReference() : null;}
+
+    /** Clear an entry that still has valid references */
+    private CountedEntry<K, V> purge() {
+        RESPIN: do {
+            int oldCount = refCount.get();
+            if (oldCount == CLEANED) return null;
+            if (oldCount < 1) throw new IllegalStateException();
+            if (!!! refCount.compareAndSet(oldCount, CLEANED)) continue RESPIN;
+            return this;
+        } while (true);
+    }
+
+    final class ValueReference implements Reference<V> {
+        private final ReferenceCloserTask closer = new ReferenceCloserTask();
+        public V get() {return value;}
+        public void close() {closer.run();}
+        CountedEntry<K, V> invalidateAndGetEntry() {return closer.purge();}
+        Runnable getCloserTask() {return closer;}
+    }
+
+    /**
+     * In order to drive cleanup after a ValueReference becomes unreachable,
+     * we need to store the clean up details in a separate object that holds
+     * no strong reference back to the ValueReference object
+     */
+    final class ReferenceCloserTask implements Runnable {
+        boolean closed;
+        public synchronized void run() {closed = closed || release();}
+        synchronized CountedEntry<K,V> purge() {
+            if (closed) throw new IllegalStateException();
+            closed = true;
+            return CountedEntry.this.purge();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Foot.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Foot.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Foot.java
new file mode 100644
index 0000000..cc433f8
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Foot.java
@@ -0,0 +1,15 @@
+package org.apache.yoko.util.concurrent;
+
+import static java.util.Objects.requireNonNull;
+
+final class Foot<T> implements NNode<T> {
+    private PNode<T> prev;
+    
+    public PNode<T> prev() {return prev;}
+    public void prev(PNode<T> pnode) {prev = pnode;}
+    
+    Foot(Head<T> head) {
+        this.prev = requireNonNull(head);
+        head.next(this);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Head.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Head.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Head.java
new file mode 100644
index 0000000..9f52e63
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/Head.java
@@ -0,0 +1,7 @@
+package org.apache.yoko.util.concurrent;
+
+final class Head<T> implements PNode<T> {
+    private NNode<T> next;
+    public NNode<T> next() {return next;}
+    public void next(NNode<T> nnode) {next = nnode;}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NNode.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NNode.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NNode.java
new file mode 100644
index 0000000..51863fd
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NNode.java
@@ -0,0 +1,7 @@
+package org.apache.yoko.util.concurrent;
+
+interface NNode<T> {
+    PNode<T> prev();
+    
+    void prev(PNode<T> pnode);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-yoko/blob/f580371d/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NoOpRunnableFactory.java
----------------------------------------------------------------------
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NoOpRunnableFactory.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NoOpRunnableFactory.java
new file mode 100644
index 0000000..6d71a32
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/NoOpRunnableFactory.java
@@ -0,0 +1,10 @@
+package org.apache.yoko.util.concurrent;
+
+import org.apache.yoko.util.Factory;
+import org.apache.yoko.util.KeyedFactory;
+
+public enum NoOpRunnableFactory implements Runnable, KeyedFactory<Object, Runnable> {
+    INSTANCE;
+    public Runnable create(Object key) {return this;}
+    public void run() {}
+}


Mime
View raw message