axis-java-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andreas Veithen <andreas.veit...@gmail.com>
Subject Re: svn commit: r1166132 - in /axis/axis2/java/core/trunk/modules: adb/src/org/apache/axis2/databinding/utils/ kernel/src/org/apache/axis2/classloader/ kernel/src/org/apache/axis2/deployment/
Date Wed, 07 Sep 2011 15:25:03 GMT
It's actually no longer (only) in my mind ;-) The implementation works
and yields a measurable performance improvement. I actually
implemented that while doing some performance tests on Synapse Sample
100 (in order to compare Axis2 performance with the standard Axiom
implementation vs. my DDOM Axiom implementation [1]). The BeanInfo
caching increases throughput by 5 to 10%, which is actually quite
significant considering that the test measures end-to-end performance
(i.e. includes the Synapse instance in front of the service), that
most of the CPU cycles are spent in WS-Security stuff and that the
service (SecureStockQuoteService) is very simple.

The most important points of the design are already described in the
Javadoc. Here are some more explanations:

* The JRE already has a BeanInfo cache, but it only caches the result
of Introspector#getBeanInfo with stopClass=null (at least in the
Sun/Oracle version).
* Implementing a cache for BeanInfo instances with stopClass!=null is
in principle pretty straightforward. However, if that is implemented
naively (i.e. as a singleton in BeanUtil), it will cause a class
loader leak if a service is undeployed. That is true even if
WeakHashMaps are used.
* I argued earlier that the natural way to avoid the class loader leak
would be to have a cache per ServiceGroupContext (or something
similar). However it turns out that this would require some
significant changes in the code (in order to pass the
ServiceGroupContext reference around), and it is not even sure that in
every place where BeanUtil is used, there is a ServiceGroupContext.
* Finally I decided to leverage the fact that all the problematic
class loaders are under Axis2's control and in general are custom
class loader implementations. This gives us the option to have a per
class loader BeanInfo cache. The idea is that each (service) class
loader has a BeanInfo cache that stores the BeanInfo objects for the
Javabean classes it has loaded itself. That cache can use strong
references without ever causing a class loader leak. There is also a
singleton BeanInfo cache for the Javabean classes loaded by the Web
application class loader (which is not under Axis2's control).
* The static BeanInfoCache#getCachedBeanInfo method contains the logic
to locate the appropriate BeanInfo cache and to query that cache.

Currently, only the DeploymentClassLoader has been modified to add the
BeanInfo cache. That works for AAR deployments but may not be enough
for other deployment scenarios. If no appropriate BeanInfo cache is
found, then a warning message is logged. If somebody sees this
warning, please let me know the deployment scenario in which this
occurs so that I can do the necessary changes to cover that scenario
as well.

Andreas

[1] http://code.google.com/p/ddom/

On Wed, Sep 7, 2011 at 13:53, Sagara Gunathunga
<sagara.gunathunga@gmail.com> wrote:
> Andreas,
>
> Would you mind to update the dev list (or one of the JIRA issue) about
> the design in your mind for this implementation ?
>
> Thanks !
>
> On Wed, Sep 7, 2011 at 5:11 PM,  <veithen@apache.org> wrote:
>> Author: veithen
>> Date: Wed Sep  7 11:41:52 2011
>> New Revision: 1166132
>>
>> URL: http://svn.apache.org/viewvc?rev=1166132&view=rev
>> Log:
>> AXIS2-4524 / AXIS2-4878 / AXIS2-5118 / AXIS2-5119: Initial implementation of a BeanInfo
cache to speed up POJO services.
>>
>> Added:
>>    axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCache.java
  (with props)
>>    axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCachingClassLoader.java
  (with props)
>> Modified:
>>    axis/axis2/java/core/trunk/modules/adb/src/org/apache/axis2/databinding/utils/BeanUtil.java
>>    axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/deployment/DeploymentClassLoader.java
>>
>> Modified: axis/axis2/java/core/trunk/modules/adb/src/org/apache/axis2/databinding/utils/BeanUtil.java
>> URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/adb/src/org/apache/axis2/databinding/utils/BeanUtil.java?rev=1166132&r1=1166131&r2=1166132&view=diff
>> ==============================================================================
>> --- axis/axis2/java/core/trunk/modules/adb/src/org/apache/axis2/databinding/utils/BeanUtil.java
(original)
>> +++ axis/axis2/java/core/trunk/modules/adb/src/org/apache/axis2/databinding/utils/BeanUtil.java
Wed Sep  7 11:41:52 2011
>> @@ -22,7 +22,6 @@ package org.apache.axis2.databinding.uti
>>
>>  import java.beans.BeanInfo;
>>  import java.beans.IntrospectionException;
>> -import java.beans.Introspector;
>>  import java.beans.PropertyDescriptor;
>>  import java.lang.reflect.Array;
>>  import java.lang.reflect.InvocationTargetException;
>> @@ -58,6 +57,7 @@ import org.apache.axiom.om.impl.dom.DOOM
>>  import org.apache.axiom.om.impl.dom.DocumentImpl;
>>  import org.apache.axiom.om.util.Base64;
>>  import org.apache.axis2.AxisFault;
>> +import org.apache.axis2.classloader.BeanInfoCache;
>>  import org.apache.axis2.context.MessageContext;
>>  import org.apache.axis2.databinding.typemapping.SimpleTypeMapper;
>>  import org.apache.axis2.databinding.utils.reader.ADBXMLStreamReaderImpl;
>> @@ -121,19 +121,7 @@ public class BeanUtil {
>>
>>
>>     private static BeanInfo getBeanInfo(Class beanClass, Class beanSuperclass)
throws IntrospectionException {
>> -        BeanInfo beanInfo;
>> -        try {
>> -            if (beanSuperclass != null)
>> -               beanInfo = Introspector.getBeanInfo(beanClass, beanSuperclass);
>> -            else
>> -                beanInfo = Introspector.getBeanInfo(beanClass);
>> -        }
>> -        catch (IntrospectionException e) {
>> -            throw e;
>> -        }
>> -
>> -
>> -        return beanInfo;
>> +        return BeanInfoCache.getCachedBeanInfo(beanClass, beanSuperclass);
>>     }
>>
>>     private static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionException
{
>>
>> Added: axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCache.java
>> URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCache.java?rev=1166132&view=auto
>> ==============================================================================
>> --- axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCache.java
(added)
>> +++ axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCache.java
Wed Sep  7 11:41:52 2011
>> @@ -0,0 +1,130 @@
>> +/*
>> + * 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.axis2.classloader;
>> +
>> +import java.beans.BeanInfo;
>> +import java.beans.IntrospectionException;
>> +import java.beans.Introspector;
>> +import java.util.Map;
>> +import java.util.concurrent.ConcurrentHashMap;
>> +
>> +import org.apache.commons.logging.Log;
>> +import org.apache.commons.logging.LogFactory;
>> +
>> +/**
>> + * {@link BeanInfo} cache that stores introspection results by bean class and stop
class. This goes
>> + * beyond the caching provided by the JRE, which only caches the results of
>> + * {@link Introspector#getBeanInfo(Class)}, but not the results of
>> + * {@link Introspector#getBeanInfo(Class, Class)} (with non null stop class).
>> + * <p>
>> + * To avoid class loader leaks, this class should not be used as a singleton if
the introspected
>> + * classes are loaded from class loaders that are children of the class loader which
holds the
>> + * reference to the cache. In such scenarios, use {@link #getCachedBeanInfo(Class,
Class)}.
>> + */
>> +public final class BeanInfoCache {
>> +    private static final class CacheKey {
>> +        private final Class<?> beanClass;
>> +        private final Class<?> stopClass;
>> +
>> +        CacheKey(Class<?> beanClass, Class<?> stopClass) {
>> +            this.beanClass = beanClass;
>> +            this.stopClass = stopClass;
>> +        }
>> +
>> +        @Override
>> +        public int hashCode() {
>> +            return 31*beanClass.hashCode() + (stopClass == null ? 0 : stopClass.hashCode());
>> +        }
>> +
>> +        @Override
>> +        public boolean equals(Object obj) {
>> +            if (obj instanceof CacheKey) {
>> +                CacheKey other = (CacheKey)obj;
>> +                return beanClass == other.beanClass && stopClass
== other.stopClass;
>> +            } else {
>> +                return false;
>> +            }
>> +        }
>> +    }
>> +
>> +    private static final Log log = LogFactory.getLog(BeanInfoCache.class);
>> +
>> +    /**
>> +     * Local cache instance for Javabeans that are loaded from the same class
loader as this class.
>> +     */
>> +    private static final BeanInfoCache localCache = new BeanInfoCache();
>> +
>> +    private final Map<CacheKey,BeanInfo> cache = new ConcurrentHashMap<CacheKey,BeanInfo>();
>> +
>> +    /**
>> +     * Introspect on a Java bean and return a cached {@link BeanInfo} object.
>> +     *
>> +     * @param beanClass
>> +     *            The bean class to be analyzed.
>> +     * @param stopClass
>> +     *            The base class at which to stop the analysis; may be <code>null</code>.
>> +     * @return A {@link BeanInfo} object describing the target bean.
>> +     * @exception IntrospectionException
>> +     *                if an exception occurs during introspection.
>> +     * @see Introspector#getBeanInfo(Class, Class)
>> +     */
>> +    public BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
throws IntrospectionException {
>> +        CacheKey key = new CacheKey(beanClass, stopClass);
>> +        BeanInfo beanInfo = cache.get(key);
>> +        if (beanInfo == null) {
>> +            beanInfo = Introspector.getBeanInfo(beanClass, stopClass);
>> +            cache.put(key, beanInfo);
>> +        }
>> +        return beanInfo;
>> +    }
>> +
>> +    /**
>> +     * Locate an appropriate {@link BeanInfoCache} and return a cached {@link
BeanInfo} object.
>> +     * This method ensures that caching the {@link BeanInfo} object will not result
in a class
>> +     * loader leak.
>> +     *
>> +     * @param beanClass
>> +     *            The bean class to be analyzed.
>> +     * @param stopClass
>> +     *            The base class at which to stop the analysis; may be <code>null</code>.
>> +     * @return A {@link BeanInfo} object describing the target bean.
>> +     * @exception IntrospectionException
>> +     *                if an exception occurs during introspection.
>> +     */
>> +    public static BeanInfo getCachedBeanInfo(Class<?> beanClass, Class<?>
stopClass) throws IntrospectionException {
>> +        ClassLoader classLoader = beanClass.getClassLoader();
>> +        BeanInfoCache cache;
>> +        if (classLoader instanceof BeanInfoCachingClassLoader) {
>> +            cache = ((BeanInfoCachingClassLoader)classLoader).getBeanInfoCache();
>> +        } else if (classLoader == BeanInfoCache.class.getClassLoader()) {
>> +            cache = localCache;
>> +        } else {
>> +            cache = null;
>> +        }
>> +        if (cache != null) {
>> +            return cache.getBeanInfo(beanClass, stopClass);
>> +        } else {
>> +            if (log.isWarnEnabled()) {
>> +                log.warn("Unable to locate a BeanInfo cache for " + beanClass
+ " (stopClass=" + stopClass
>> +                        + "). This will negatively affect performance!");
>> +            }
>> +            return Introspector.getBeanInfo(beanClass, stopClass);
>> +        }
>> +    }
>> +}
>>
>> Propchange: axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCache.java
>> ------------------------------------------------------------------------------
>>    svn:eol-style = native
>>
>> Added: axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCachingClassLoader.java
>> URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCachingClassLoader.java?rev=1166132&view=auto
>> ==============================================================================
>> --- axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCachingClassLoader.java
(added)
>> +++ axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCachingClassLoader.java
Wed Sep  7 11:41:52 2011
>> @@ -0,0 +1,33 @@
>> +/*
>> + * 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.axis2.classloader;
>> +
>> +import java.beans.BeanInfo;
>> +
>> +/**
>> + * Interface implemented by class loaders that keep a {@link BeanInfo} cache.
>> + */
>> +public interface BeanInfoCachingClassLoader {
>> +    /**
>> +     * Get the {@link BeanInfo} cache for this class loader.
>> +     *
>> +     * @return the cache instance; must not be <code>null</code>
>> +     */
>> +    BeanInfoCache getBeanInfoCache();
>> +}
>>
>> Propchange: axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/classloader/BeanInfoCachingClassLoader.java
>> ------------------------------------------------------------------------------
>>    svn:eol-style = native
>>
>> Modified: axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/deployment/DeploymentClassLoader.java
>> URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/deployment/DeploymentClassLoader.java?rev=1166132&r1=1166131&r2=1166132&view=diff
>> ==============================================================================
>> --- axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/deployment/DeploymentClassLoader.java
(original)
>> +++ axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/deployment/DeploymentClassLoader.java
Wed Sep  7 11:41:52 2011
>> @@ -20,6 +20,8 @@
>>  package org.apache.axis2.deployment;
>>
>>  import org.apache.axiom.attachments.utils.IOUtils;
>> +import org.apache.axis2.classloader.BeanInfoCache;
>> +import org.apache.axis2.classloader.BeanInfoCachingClassLoader;
>>
>>  import java.io.ByteArrayInputStream;
>>  import java.io.IOException;
>> @@ -36,7 +38,7 @@ import java.util.List;
>>  import java.util.zip.ZipEntry;
>>  import java.util.zip.ZipInputStream;
>>
>> -public class DeploymentClassLoader extends URLClassLoader {
>> +public class DeploymentClassLoader extends URLClassLoader implements BeanInfoCachingClassLoader
{
>>     // List of URL's
>>     private URL[] urls = null;
>>
>> @@ -45,6 +47,8 @@ public class DeploymentClassLoader exten
>>
>>     private boolean isChildFirstClassLoading;
>>
>> +    private final BeanInfoCache beanInfoCache = new BeanInfoCache();
>> +
>>     /**
>>      * DeploymentClassLoader is extended from URLClassLoader. The constructor
>>      * does not override the super constructor, but takes in an addition list
of
>> @@ -291,4 +295,8 @@ public class DeploymentClassLoader exten
>>     public void setChildFirstClassLoading(boolean childFirstClassLoading) {
>>         isChildFirstClassLoading = childFirstClassLoading;
>>     }
>> +
>> +    public final BeanInfoCache getBeanInfoCache() {
>> +        return beanInfoCache;
>> +    }
>>  }
>>
>>
>>
>
>
>
> --
> Sagara Gunathunga
>
> Blog      - http://ssagara.blogspot.com
> Web      - http://people.apache.org/~sagara/
> LinkedIn - http://www.linkedin.com/in/ssagara
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: java-dev-unsubscribe@axis.apache.org
> For additional commands, e-mail: java-dev-help@axis.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: java-dev-unsubscribe@axis.apache.org
For additional commands, e-mail: java-dev-help@axis.apache.org


Mime
View raw message