You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2016/09/25 22:06:58 UTC

svn commit: r1762242 - in /felix/trunk/framework/src/main/java/org/apache/felix/framework: URLHandlers.java URLHandlersBundleStreamHandler.java URLHandlersContentHandlerProxy.java URLHandlersStreamHandlerProxy.java util/Util.java

Author: pauls
Date: Sun Sep 25 22:06:58 2016
New Revision: 1762242

URL: http://svn.apache.org/viewvc?rev=1762242&view=rev
Log:
Use less locking in URL handlers in order to speed up urls under concurrency (FELIX-5363).

Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/util/Util.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java?rev=1762242&r1=1762241&r2=1762242&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java Sun Sep 25 22:06:58 2016
@@ -24,11 +24,14 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
 import java.net.URLStreamHandlerFactory;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import static org.apache.felix.framework.util.Util.putIfAbsentAndReturn;
 
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.SecureAction;
@@ -87,22 +90,24 @@ class URLHandlers implements URLStreamHa
 
     // This maps classloaders of URLHandlers in other classloaders to lists of
     // their frameworks.
-    private final static Map m_classloaderToFrameworkLists = new HashMap();
+    private final static ConcurrentHashMap<ClassLoader, List<Object>> m_classloaderToFrameworkLists = new ConcurrentHashMap<ClassLoader, List<Object>>();
 
     // The list to hold all enabled frameworks registered with this handlers
-    private static final List m_frameworks = new ArrayList();
-    private static int m_counter = 0;
+    private static final CopyOnWriteArrayList m_frameworks = new CopyOnWriteArrayList();
+    private static volatile int m_counter = 0;
+
+    private static final ConcurrentHashMap<String, ContentHandler> m_contentHandlerCache = new ConcurrentHashMap<String, ContentHandler>();
+    private static final ConcurrentHashMap<String, URLStreamHandler> m_streamHandlerCache = new ConcurrentHashMap<String, URLStreamHandler>();
+    private static final ConcurrentHashMap<URLStreamHandler, URL> m_handlerToURL = new ConcurrentHashMap<URLStreamHandler, URL>();
 
-    private static Map m_contentHandlerCache = null;
-    private static Map m_streamHandlerCache = null;
-    private static URLStreamHandlerFactory m_streamHandlerFactory;
-    private static ContentHandlerFactory m_contentHandlerFactory;
+    private static volatile URLStreamHandlerFactory m_streamHandlerFactory;
+    private static volatile ContentHandlerFactory m_contentHandlerFactory;
     private static final String STREAM_HANDLER_PACKAGE_PROP = "java.protocol.handler.pkgs";
     private static final String DEFAULT_STREAM_HANDLER_PACKAGE = "sun.net.www.protocol|com.ibm.oti.net.www.protocol|gnu.java.net.protocol|wonka.net|com.acunia.wonka.net|org.apache.harmony.luni.internal.net.www.protocol|weblogic.utils|weblogic.net|javax.net.ssl|COM.newmonics.www.protocols";
-    private static Object m_rootURLHandlers;
+    private static volatile Object m_rootURLHandlers;
 
     private static final String m_streamPkgs;
-    private static final Map m_builtIn = new HashMap();
+    private static final ConcurrentHashMap<String, URLStreamHandler> m_builtIn = new ConcurrentHashMap<String, URLStreamHandler>();
     private static final boolean m_loaded;
 
     static
@@ -115,8 +120,6 @@ class URLHandlers implements URLStreamHa
             (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class);
     }
 
-
-    private static final Map m_handlerToURL = new HashMap();
     private void init(String protocol, URLStreamHandlerFactory factory)
     {
         try
@@ -125,7 +128,7 @@ class URLHandlers implements URLStreamHa
             if (handler != null)
             {
                 URL url = new URL(protocol, null, -1, "", handler);
-                m_handlerToURL.put(handler, url);
+                addToCache(m_handlerToURL, handler, url);
             }
         }
         catch (Throwable ex)
@@ -354,29 +357,31 @@ class URLHandlers implements URLStreamHa
 
     private URLStreamHandler getBuiltInStreamHandler(String protocol, URLStreamHandlerFactory factory)
     {
-        synchronized (m_builtIn)
+        URLStreamHandler handler = getFromCache(m_builtIn, protocol);
+
+        if (handler != null)
         {
-            if (m_builtIn.containsKey(protocol))
-            {
-                return (URLStreamHandler) m_builtIn.get(protocol);
-            }
+            return handler;
         }
+
         if (factory != null)
         {
-            URLStreamHandler result = factory.createURLStreamHandler(protocol);
-            if (result != null)
-            {
-                return addToCache(protocol, result);
-            }
+            handler = factory.createURLStreamHandler(protocol);
+        }
+
+        if (handler == null)
+        {
+            // Check for built-in handlers for the mime type.
+            // Iterate over built-in packages.
+            handler = loadBuiltInStreamHandler(protocol, null);
         }
-        // Check for built-in handlers for the mime type.
-        // Iterate over built-in packages.
-        URLStreamHandler handler = loadBuiltInStreamHandler(protocol, null);
+
         if (handler == null)
         {
             handler = loadBuiltInStreamHandler(protocol, ClassLoader.getSystemClassLoader());
         }
-        return addToCache(protocol, handler);
+
+        return addToCache(m_builtIn, protocol, handler);
     }
 
     private URLStreamHandler loadBuiltInStreamHandler(String protocol, ClassLoader classLoader) {
@@ -445,16 +450,6 @@ class URLHandlers implements URLStreamHa
         return null;
     }
 
-    private synchronized URLStreamHandler addToCache(String protocol, URLStreamHandler result)
-    {
-        if (!m_builtIn.containsKey(protocol))
-            {
-                m_builtIn.put(protocol, result);
-                return result;
-            }
-        return (URLStreamHandler) m_builtIn.get(protocol);
-    }
-
     /**
      * <p>
      * This is a method implementation for the <tt>URLStreamHandlerFactory</tt>
@@ -473,7 +468,7 @@ class URLHandlers implements URLStreamHa
         // performed for code consistency between stream and content
         // handlers and also because caching behavior may not be guaranteed
         // across different JRE implementations.
-        URLStreamHandler handler = getFromStreamCache(protocol);
+        URLStreamHandler handler = getFromCache(m_streamHandlerCache, protocol);
 
         if (handler != null)
         {
@@ -484,17 +479,17 @@ class URLHandlers implements URLStreamHa
         // allowed to deal with it.
         if (protocol.equals(FelixConstants.BUNDLE_URL_PROTOCOL))
         {
-            return addToStreamCache(protocol,
+            return addToCache(m_streamHandlerCache, protocol,
                 new URLHandlersBundleStreamHandler(m_secureAction));
         }
 
-       handler = getBuiltInStreamHandler(protocol,
-           (m_streamHandlerFactory != this) ? m_streamHandlerFactory : null);
+        handler = getBuiltInStreamHandler(protocol,
+            (m_streamHandlerFactory != this) ? m_streamHandlerFactory : null);
 
         // If built-in content handler, then create a proxy handler.
-        return addToStreamCache(protocol,
+        return addToCache(m_streamHandlerCache, protocol,
             new URLHandlersStreamHandlerProxy(protocol, m_secureAction,
-                handler, (URL) m_handlerToURL.get(handler)));
+                handler, getFromCache(m_handlerToURL,handler)));
     }
 
     /**
@@ -515,63 +510,26 @@ class URLHandlers implements URLStreamHa
         // performed for code consistency between stream and content
         // handlers and also because caching behavior may not be guaranteed
         // across different JRE implementations.
-        ContentHandler handler = getFromContentCache(mimeType);
+        ContentHandler handler = getFromCache(m_contentHandlerCache, mimeType);
 
         if (handler != null)
         {
             return handler;
         }
 
-        return addToContentCache(mimeType,
+        return addToCache(m_contentHandlerCache, mimeType,
             new URLHandlersContentHandlerProxy(mimeType, m_secureAction,
             (m_contentHandlerFactory != this) ? m_contentHandlerFactory : null));
     }
 
-    private synchronized ContentHandler addToContentCache(String mimeType, ContentHandler handler)
-    {
-        if (m_contentHandlerCache == null)
-        {
-            m_contentHandlerCache = new HashMap();
-        }
-        return (ContentHandler) addToCache(m_contentHandlerCache, mimeType, handler);
-    }
-
-    private synchronized ContentHandler getFromContentCache(String mimeType)
-    {
-        return (ContentHandler) ((m_contentHandlerCache != null) ?
-            m_contentHandlerCache.get(mimeType) : null);
-    }
-
-    private synchronized URLStreamHandler addToStreamCache(String protocol, URLStreamHandler handler)
+    private static <K,V> V addToCache(ConcurrentHashMap<K,V> cache, K key, V value)
     {
-        if (m_streamHandlerCache == null)
-        {
-            m_streamHandlerCache = new HashMap();
-        }
-        return (URLStreamHandler) addToCache(m_streamHandlerCache, protocol, handler);
+        return key != null && value != null ? putIfAbsentAndReturn(cache, key, value) : null;
     }
 
-    private synchronized URLStreamHandler getFromStreamCache(String protocol)
+    private static <K,V> V getFromCache(ConcurrentHashMap<K,V> cache, K key)
     {
-        return (URLStreamHandler) ((m_streamHandlerCache != null) ?
-            m_streamHandlerCache.get(protocol) : null);
-    }
-
-    private Object addToCache(Map cache, String key, Object value)
-    {
-        if (value == null)
-        {
-            return null;
-        }
-
-        Object result = cache.get(key);
-
-        if (result == null)
-        {
-            cache.put(key, value);
-            result = value;
-        }
-        return result;
+        return key != null ? cache.get(key) : null;
     }
 
     /**
@@ -709,20 +667,22 @@ class URLHandlers implements URLStreamHa
     **/
     public static Object getFrameworkFromContext()
     {
-        // This is a hack. The idea is to return the only registered framework
-        synchronized (m_classloaderToFrameworkLists)
+        // This is a hack. The idea is to return the only registered framework quickly
+        int attempts = 0;
+        while (m_classloaderToFrameworkLists.isEmpty() && (m_counter == 1) && (m_frameworks.size() == 1))
         {
-            if (m_classloaderToFrameworkLists.isEmpty())
+            Object framework = m_frameworks.get(0);
+
+            if (framework != null)
             {
-                synchronized (m_frameworks)
-                {
-                    if ((m_counter == 1) && (m_frameworks.size() == 1))
-                    {
-                        return m_frameworks.get(0);
-                    }
-                }
+                return framework;
+            }
+            else if (attempts++ > 3)
+            {
+                break;
             }
         }
+
         // get the current class call stack.
         Class[] stack = m_sm.getClassContext();
         // Find the first class that is loaded from a bundle.
@@ -747,44 +707,36 @@ class URLHandlers implements URLStreamHa
         // the bundle that loaded the class.
         if (targetClass != null)
         {
-            synchronized (m_classloaderToFrameworkLists)
-            {
-                ClassLoader index = targetClass.getClassLoader().getClass().getClassLoader();
+            ClassLoader index = targetClass.getClassLoader().getClass().getClassLoader();
 
-                List frameworks = (List) m_classloaderToFrameworkLists.get(
-                    index);
+            List frameworks = (List) m_classloaderToFrameworkLists.get(index);
 
-                if ((frameworks == null) && (index == URLHANDLERS_CLASS.getClassLoader()))
-                {
-                    frameworks = m_frameworks;
-                }
-                if (frameworks != null)
+            if ((frameworks == null) && (index == URLHANDLERS_CLASS.getClassLoader()))
+            {
+                frameworks = m_frameworks;
+            }
+            if (frameworks != null)
+            {
+                // Check the registry of framework instances
+                for (Object framework : frameworks)
                 {
-                    synchronized (frameworks)
+                    try
                     {
-                        // Check the registry of framework instances
-                        for (int i = 0; i < frameworks.size(); i++)
+                        if (m_secureAction.invoke(
+                            m_secureAction.getDeclaredMethod(framework.getClass(),
+                            "getBundle", CLASS_TYPE),
+                            framework, new Object[]{targetClass}) != null)
                         {
-                            Object framework = frameworks.get(i);
-                            try
-                            {
-                                if (m_secureAction.invoke(
-                                    m_secureAction.getDeclaredMethod(framework.getClass(),
-                                    "getBundle", CLASS_TYPE),
-                                    framework, new Object[]{targetClass}) != null)
-                                {
-                                    return framework;
-                                }
-                            }
-                            catch (Exception ex)
-                            {
-                                // This should not happen but if it does there is
-                                // not much we can do other then ignore it.
-                                // Maybe log this or something.
-                                ex.printStackTrace();
-                            }
+                            return framework;
                         }
                     }
+                    catch (Exception ex)
+                    {
+                        // This should not happen but if it does there is
+                        // not much we can do other then ignore it.
+                        // Maybe log this or something.
+                        ex.printStackTrace();
+                    }
                 }
             }
         }

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java?rev=1762242&r1=1762241&r2=1762242&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java Sun Sep 25 22:06:58 2016
@@ -1,4 +1,4 @@
-/* 
+/*
  * 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
@@ -44,7 +44,7 @@ class URLHandlersBundleStreamHandler ext
         m_action = action;
     }
 
-    protected synchronized URLConnection openConnection(URL url) throws IOException
+    protected URLConnection openConnection(URL url) throws IOException
     {
         if (!"felix".equals(url.getAuthority()))
         {
@@ -54,9 +54,9 @@ class URLHandlersBundleStreamHandler ext
         {
             return new URLHandlersBundleURLConnection(url, m_framework);
         }
-        
+
         Object framework = URLHandlers.getFrameworkFromContext();
-        
+
         if (framework != null)
         {
             if (framework instanceof Felix)
@@ -67,8 +67,8 @@ class URLHandlersBundleStreamHandler ext
             {
                 Class targetClass = framework.getClass().getClassLoader().loadClass(
                     URLHandlersBundleURLConnection.class.getName());
-                
-                Constructor constructor = m_action.getConstructor(targetClass, 
+
+                Constructor constructor = m_action.getConstructor(targetClass,
                         new Class[]{URL.class, framework.getClass().getClassLoader().loadClass(
                                 Felix.class.getName())});
                 m_action.setAccesssible(constructor);
@@ -82,7 +82,7 @@ class URLHandlersBundleStreamHandler ext
         throw new IOException("No framework context found");
     }
 
-    protected void parseURL(URL u, String spec, int start, int limit) 
+    protected void parseURL(URL u, String spec, int start, int limit)
     {
         super.parseURL(u, spec, start, limit);
 
@@ -92,31 +92,31 @@ class URLHandlersBundleStreamHandler ext
         }
     }
 
-    protected String toExternalForm(URL u) 
+    protected String toExternalForm(URL u)
     {
-        StringBuffer result = new StringBuffer();
+        StringBuilder result = new StringBuilder();
         result.append(u.getProtocol());
         result.append("://");
         result.append(u.getHost());
         result.append(':');
         result.append(u.getPort());
-        if (u.getPath() != null) 
+        if (u.getPath() != null)
         {
             result.append(u.getPath());
         }
-        if (u.getQuery() != null) 
+        if (u.getQuery() != null)
         {
             result.append('?');
             result.append(u.getQuery());
         }
-        if (u.getRef() != null) 
+        if (u.getRef() != null)
         {
             result.append("#");
             result.append(u.getRef());
         }
         return result.toString();
     }
-    
+
     protected java.net.InetAddress getHostAddress(URL u)
     {
         return null;

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java?rev=1762242&r1=1762241&r2=1762242&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java Sun Sep 25 22:06:58 2016
@@ -22,11 +22,11 @@ import java.io.IOException;
 import java.net.ContentHandler;
 import java.net.ContentHandlerFactory;
 import java.net.URLConnection;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.felix.framework.util.SecureAction;
+import static org.apache.felix.framework.util.Util.putIfAbsentAndReturn;
 
 /**
  * <p>
@@ -57,7 +57,7 @@ class URLHandlersContentHandlerProxy ext
     private static final String CONTENT_HANDLER_PACKAGE_PROP = "java.content.handler.pkgs";
     private static final String DEFAULT_CONTENT_HANDLER_PACKAGE = "sun.net.www.content|com.ibm.oti.net.www.content|gnu.java.net.content|org.apache.harmony.luni.internal.net.www.content|COM.newmonics.www.content";
 
-    private static final Map m_builtIn = new HashMap();
+    private static final ConcurrentHashMap<String, ContentHandler> m_builtIn = new ConcurrentHashMap<String, ContentHandler>();
     private static final String m_pkgs;
 
     static
@@ -141,19 +141,19 @@ class URLHandlersContentHandlerProxy ext
 
     private ContentHandler getBuiltIn()
     {
-        synchronized (m_builtIn)
+        ContentHandler result = m_builtIn.get(m_mimeType);
+
+        if (result == null)
         {
-            if (m_builtIn.containsKey(m_mimeType))
-            {
-                return (ContentHandler) m_builtIn.get(m_mimeType);
-            }
+            return result;
         }
+
         if (m_factory != null)
         {
-            ContentHandler result = m_factory.createContentHandler(m_mimeType);
+            result = m_factory.createContentHandler(m_mimeType);
             if (result != null)
             {
-                return addToCache(m_mimeType, result);
+                return putIfAbsentAndReturn(m_builtIn, m_mimeType, result);
             }
         }
         // Check for built-in handlers for the mime type.
@@ -172,7 +172,7 @@ class URLHandlersContentHandlerProxy ext
                 Class handler = m_action.forName(className, null);
                 if (handler != null)
                 {
-                    return addToCache(m_mimeType,
+                    return putIfAbsentAndReturn(m_builtIn, m_mimeType,
                         (ContentHandler) handler.newInstance());
                 }
             }
@@ -183,16 +183,6 @@ class URLHandlersContentHandlerProxy ext
                 // case other than ignore it.
             }
         }
-        return addToCache(m_mimeType, null);
-    }
-
-    private synchronized ContentHandler addToCache(String mimeType, ContentHandler handler)
-    {
-        if (!m_builtIn.containsKey(mimeType))
-        {
-            m_builtIn.put(mimeType, handler);
-            return handler;
-        }
-        return (ContentHandler) m_builtIn.get(mimeType);
+        return null;
     }
 }
\ No newline at end of file

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java?rev=1762242&r1=1762241&r2=1762242&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java Sun Sep 25 22:06:58 2016
@@ -526,7 +526,7 @@ public class URLHandlersStreamHandlerPro
             // case it can -- hence, we catch the NPE and do the work
             // ourselvs. The only difference is that we check whether the
             // URL.getFile() is null or not.
-            StringBuffer answer = new StringBuffer();
+            StringBuilder answer = new StringBuilder();
             answer.append(url.getProtocol());
             answer.append(':');
             String authority = url.getAuthority();
@@ -572,14 +572,14 @@ public class URLHandlersStreamHandlerPro
         {
             // Get the framework instance associated with call stack.
             Object framework = URLHandlers.getFrameworkFromContext();
-         
+
             if (framework == null)
             {
                 return m_builtIn;
             }
 
 
-            Object service = null;
+            Object service;
             if (framework instanceof Felix)
             {
                 service = ((Felix) framework).getStreamHandlerService(m_protocol);
@@ -612,7 +612,7 @@ public class URLHandlersStreamHandlerPro
         {
             // In case that we are inside tomcat - the problem is that the webapp classloader
             // creates a new url to load a class. This gets us to this method. Now, if we
-            // trigger a classload while executing tomcat is creating a new url and we end-up with
+            // trigger a classload while executing, tomcat is creating a new url and we end-up with
             // a loop which is cut short after two iterations (because of a circularclassload).
             // We catch this exception (and all others) and just return the built-in handler
             // (if we have any) as this way we at least eventually get started (this just means
@@ -624,28 +624,20 @@ public class URLHandlersStreamHandlerPro
     public Object invoke(Object obj, Method method, Object[] params)
         throws Throwable
     {
-        try
+        Class[] types = method.getParameterTypes();
+        if (m_service == null)
         {
-
-            Class[] types = method.getParameterTypes();
-            if (m_service == null)
-            {
-                return m_action.invoke(m_action.getMethod(this.getClass(), method.getName(), types), this, params);
-            }
-            if ("parseURL".equals(method.getName()))
-            {
-                types[0] = m_service.getClass().getClassLoader().loadClass(
-                    URLStreamHandlerSetter.class.getName());
-                params[0] = Proxy.newProxyInstance(
-                    m_service.getClass().getClassLoader(), new Class[]{types[0]},
-                    (URLHandlersStreamHandlerProxy) params[0]);
-            }
-            return m_action.invokeDirect(m_action.getDeclaredMethod(m_service.getClass(),
-                method.getName(), types), m_service, params);
+            return m_action.invoke(m_action.getMethod(this.getClass(), method.getName(), types), this, params);
         }
-        catch (Exception ex)
+        if ("parseURL".equals(method.getName()))
         {
-            throw ex;
+            types[0] = m_service.getClass().getClassLoader().loadClass(
+                URLStreamHandlerSetter.class.getName());
+            params[0] = Proxy.newProxyInstance(
+                m_service.getClass().getClassLoader(), new Class[]{types[0]},
+                (URLHandlersStreamHandlerProxy) params[0]);
         }
+        return m_action.invokeDirect(m_action.getDeclaredMethod(m_service.getClass(),
+            method.getName(), types), m_service, params);
     }
 }

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/util/Util.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/util/Util.java?rev=1762242&r1=1762241&r2=1762242&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/util/Util.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/util/Util.java Sun Sep 25 22:06:58 2016
@@ -33,6 +33,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.felix.framework.Logger;
 import org.osgi.framework.Bundle;
@@ -831,4 +832,10 @@ public class Util
         builder.insert(23, '-');
         return builder.toString();
     }
+
+    public static <K,V> V putIfAbsentAndReturn(ConcurrentHashMap<K,V> map, K key, V value)
+    {
+        V result = map.putIfAbsent(key, value);
+        return result != null ? result : value;
+    }
 }
\ No newline at end of file