Freeman,

This looks much better.   Can you get this merged in?  Thanks!

Dan


> On Dec 24, 2018, at 4:50 AM, Freeman Fang <[email protected]> wrote:
> 
> Hi Dan,
> 
> I just revised to use ClassValue as ProxyClassLoaderCache backend, and I 
> pushed the changes to ProxyClassLoaderCache branch, would you please take a 
> look?
> 
> Merry Christmas!
> -------------
> Freeman(Yue) Fang
> 
> Red Hat, Inc. 
> 
> 
> 
> 
> 
>> On Dec 21, 2018, at 11:43 PM, Daniel Kulp <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> This commit has a bunch of problems.   
>> 
>> At no point is anything removed from these caches until the cache fills.  
>> Thus, would definitely be considered a memory leak of some sort.
>> 
>> Also, because the class loader is held onto strongly, the classes are locked 
>> and not able to be garbage collected.   This would prevent apps from being 
>> unloaded, result in locked jar/wars/ears, etc…
>> 
>> This really needs to be reverted before 3.3.
>> 
>> My suggestion, I think, would be if you can determine which interface in the 
>> array is the user one (and not CXF/JDK), use a ClassValue<ClassLoader> to 
>> compute and hold this.  
>> 
>> 
>> 
>> Dan
>> 
>> 
>> 
>>> On Dec 21, 2018, at 12:32 AM, [email protected] <mailto:[email protected]> 
>>> wrote:
>>> 
>>> This is an automated email from the ASF dual-hosted git repository.
>>> 
>>> ffang pushed a commit to branch master
>>> in repository https://gitbox.apache.org/repos/asf/cxf.git 
>>> <https://gitbox.apache.org/repos/asf/cxf.git>
>>> 
>>> 
>>> The following commit(s) were added to refs/heads/master by this push:
>>>    new af33b2a  [CXF-7934]Add cache for ProxyClassLoader
>>> af33b2a is described below
>>> 
>>> commit af33b2a28c39504388ea370f6849a06d551b4dd1
>>> Author: Freeman Fang <[email protected] 
>>> <mailto:[email protected]>>
>>> AuthorDate: Fri Dec 21 13:32:19 2018 +0800
>>> 
>>>   [CXF-7934]Add cache for ProxyClassLoader
>>> ---
>>> .../org/apache/cxf/common/util/ProxyHelper.java    | 53 
>>> +++++++++++++++++++++-
>>> .../org/apache/cxf/jaxrs/utils/InjectionUtils.java | 50 +++++++++++++++++---
>>> 2 files changed, 95 insertions(+), 8 deletions(-)
>>> 
>>> diff --git a/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java 
>>> b/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java
>>> index 7b185aa..ad83ad9 100644
>>> --- a/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java
>>> +++ b/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java
>>> @@ -24,6 +24,13 @@ import java.lang.reflect.Method;
>>> import java.lang.reflect.Proxy;
>>> import java.security.AccessController;
>>> import java.security.PrivilegedAction;
>>> +import java.util.Collections;
>>> +import java.util.HashMap;
>>> +import java.util.Map;
>>> +import java.util.logging.Level;
>>> +import java.util.logging.Logger;
>>> +
>>> +import org.apache.cxf.common.logging.LogUtils;
>>> 
>>> /**
>>> *
>>> @@ -39,8 +46,15 @@ public class ProxyHelper {
>>>        }
>>>        HELPER = theHelper;
>>>    }
>>> -
>>> -
>>> +   
>>> +    private static final Logger LOG = 
>>> LogUtils.getL7dLogger(ProxyHelper.class);
>>> +    
>>> +    protected Map<String, ClassLoader> proxyClassLoaderCache = 
>>> +        Collections.synchronizedMap(new HashMap<String, ClassLoader>());
>>> +    protected int cacheSize =
>>> +        
>>> Integer.parseInt(System.getProperty("org.apache.cxf.proxy.classloader.size",
>>>  "3000"));
>>> +    
>>> +    
>>>    protected ProxyHelper() {
>>>    }
>>> 
>>> @@ -59,9 +73,26 @@ public class ProxyHelper {
>>>     */
>>>    private ClassLoader getClassLoaderForInterfaces(final ClassLoader 
>>> loader, final Class<?>[] interfaces) {
>>>        if (canSeeAllInterfaces(loader, interfaces)) {
>>> +            LOG.log(Level.FINE, "current classloader " + loader + " can 
>>> see all interface");
>>>            return loader;
>>>        }
>>> +        String sortedNameFromInterfaceArray = 
>>> getSortedNameFromInterfaceArray(interfaces);
>>> +        ClassLoader cachedLoader = 
>>> proxyClassLoaderCache.get(sortedNameFromInterfaceArray);
>>> +        if (cachedLoader != null) {
>>> +            if (canSeeAllInterfaces(cachedLoader, interfaces)) {
>>> +                //found cached loader
>>> +                LOG.log(Level.FINE, "find required loader from 
>>> ProxyClassLoader cache with key" 
>>> +                        + sortedNameFromInterfaceArray);
>>> +                return cachedLoader;
>>> +            } else {
>>> +                //found cached loader somehow can't see all interfaces
>>> +                LOG.log(Level.FINE, "find a loader from ProxyClassLoader 
>>> cache with key " 
>>> +                        + sortedNameFromInterfaceArray
>>> +                        + " but can't see all interfaces");
>>> +            }
>>> +        }
>>>        ProxyClassLoader combined;
>>> +        LOG.log(Level.FINE, "can't find required ProxyClassLoader from 
>>> cache, create a new one with parent " + loader);
>>>        final SecurityManager sm = System.getSecurityManager();
>>>        if (sm == null) {
>>>            combined = new ProxyClassLoader(loader, interfaces);
>>> @@ -75,9 +106,26 @@ public class ProxyHelper {
>>>        }
>>>        for (Class<?> currentInterface : interfaces) {
>>>            combined.addLoader(getClassLoader(currentInterface));
>>> +            LOG.log(Level.FINE, "interface for new created 
>>> ProxyClassLoader is "
>>> +                + currentInterface.getName());
>>> +            LOG.log(Level.FINE, "interface's classloader for new created 
>>> ProxyClassLoader is "
>>> +                + currentInterface.getClassLoader());
>>>        }
>>> +        if (proxyClassLoaderCache.size() >= cacheSize) {
>>> +            LOG.log(Level.FINE, "proxyClassLoaderCache is full, need clear 
>>> it");
>>> +            proxyClassLoaderCache.clear();
>>> +        }
>>> +        proxyClassLoaderCache.put(sortedNameFromInterfaceArray, combined);
>>>        return combined;
>>>    }
>>> +    
>>> +    private String getSortedNameFromInterfaceArray(Class<?>[] interfaces) {
>>> +        SortedArraySet<String> arraySet = new SortedArraySet<String>();
>>> +        for (Class<?> currentInterface : interfaces) {
>>> +            arraySet.add(currentInterface.getName() + 
>>> currentInterface.getClassLoader());
>>> +        }
>>> +        return arraySet.toString();
>>> +    }
>>> 
>>>    private static ClassLoader getClassLoader(final Class<?> clazz) {
>>>        final SecurityManager sm = System.getSecurityManager();
>>> @@ -123,4 +171,5 @@ public class ProxyHelper {
>>>    public static Object getProxy(ClassLoader loader, Class<?>[] interfaces, 
>>> InvocationHandler handler) {
>>>        return HELPER.getProxyInternal(loader, interfaces, handler);
>>>    }
>>> +    
>>> }
>>> diff --git 
>>> a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
>>>  
>>> b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
>>> index ce4d239..e2c80e1 100644
>>> --- 
>>> a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
>>> +++ 
>>> b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
>>> @@ -137,7 +137,13 @@ public final class InjectionUtils {
>>>    private static final String ENUM_CONVERSION_CASE_SENSITIVE = 
>>> "enum.conversion.case.sensitive";
>>> 
>>>    private static final String IGNORE_MATRIX_PARAMETERS = 
>>> "ignore.matrix.parameters";
>>> -
>>> +    
>>> +    private static Map<String, ProxyClassLoader> proxyClassLoaderCache = 
>>> +        Collections.synchronizedMap(new HashMap<String, 
>>> ProxyClassLoader>());
>>> +    
>>> +    private static int cacheSize =
>>> +        
>>> Integer.parseInt(System.getProperty("org.apache.cxf.proxy.classloader.size",
>>>  "3000"));
>>> +    
>>>    private InjectionUtils() {
>>> 
>>>    }
>>> @@ -1076,9 +1082,25 @@ public final class InjectionUtils {
>>>            proxy = createThreadLocalServletApiContext(type.getName());
>>>        }
>>>        if (proxy == null) {
>>> -            ProxyClassLoader loader = new 
>>> ProxyClassLoader(Proxy.class.getClassLoader());
>>> -            loader.addLoader(type.getClassLoader());
>>> -            loader.addLoader(ThreadLocalProxy.class.getClassLoader());
>>> +            ProxyClassLoader loader
>>> +                = proxyClassLoaderCache.get(type.getName() + 
>>> type.getClassLoader()); 
>>> +            if (loader == null
>>> +                || !canSeeAllClasses(loader, new Class<?>[]{Proxy.class, 
>>> type, ThreadLocalProxy.class})) {
>>> +                // to avoid creating too much ProxyClassLoader to save 
>>> Metaspace usage
>>> +                LOG.log(Level.FINE, "can't find required ProxyClassLoader 
>>> for type " + type.getName());
>>> +                LOG.log(Level.FINE, "create a new one with parent  " + 
>>> Proxy.class.getClassLoader());
>>> +                loader = new 
>>> ProxyClassLoader(Proxy.class.getClassLoader());
>>> +                loader.addLoader(type.getClassLoader());
>>> +                LOG.log(Level.FINE, "type classloader is  " + 
>>> type.getClassLoader());
>>> +                loader.addLoader(ThreadLocalProxy.class.getClassLoader());
>>> +                LOG.log(Level.FINE, "ThreadLocalProxy classloader is  " 
>>> +                    + 
>>> ThreadLocalProxy.class.getClassLoader().getClass().getName());
>>> +                if (proxyClassLoaderCache.size() >= cacheSize) {
>>> +                    LOG.log(Level.FINE, "proxyClassLoaderCache is full, 
>>> need clear it");
>>> +                    proxyClassLoaderCache.clear();
>>> +                }
>>> +                proxyClassLoaderCache.put(type.getName() + 
>>> type.getClassLoader(), loader); 
>>> +            } 
>>>            return (ThreadLocalProxy<T>)Proxy.newProxyInstance(loader,
>>>                                   new Class[] {type, ThreadLocalProxy.class 
>>> },
>>>                                   new ThreadLocalInvocationHandler<T>());
>>> @@ -1086,8 +1108,24 @@ public final class InjectionUtils {
>>> 
>>>        return (ThreadLocalProxy<T>)proxy;
>>>    }
>>> -
>>> -    private static boolean isServletApiContext(String name) {
>>> +    
>>> +    private static boolean canSeeAllClasses(ClassLoader loader, Class<?>[] 
>>> interfaces) {
>>> +        for (Class<?> currentInterface : interfaces) {
>>> +            String ifName = currentInterface.getName();
>>> +            try {
>>> +                Class<?> ifClass = Class.forName(ifName, true, loader);
>>> +                if (ifClass != currentInterface) {
>>> +                    return false;
>>> +                }
>>> +               
>>> +            } catch (NoClassDefFoundError | ClassNotFoundException e) {
>>> +                return false;
>>> +            }
>>> +        }
>>> +        return true;
>>> +    }
>>> +    
>>> +    private static boolean isServletApiContext(String name) { 
>>>        return name.startsWith("javax.servlet.");
>>>    }
>>> 
>>> 
>> 
>> -- 
>> Daniel Kulp
>> [email protected] <mailto:[email protected]> <mailto:[email protected] 
>> <mailto:[email protected]>> - http://dankulp.com/blog 
>> <http://dankulp.com/blog> <http://dankulp.com/blog <http://dankulp.com/blog>>
>> Talend Community Coder - http://talend.com <http://talend.com/> 
>> <http://coders.talend.com/ <http://coders.talend.com/>>

-- 
Daniel Kulp
[email protected] <mailto:[email protected]> - http://dankulp.com/blog 
<http://dankulp.com/blog>
Talend Community Coder - http://talend.com <http://coders.talend.com/>

Reply via email to