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.gunathu...@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, <veit...@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-unsubscr...@axis.apache.org > For additional commands, e-mail: java-dev-h...@axis.apache.org > > --------------------------------------------------------------------- To unsubscribe, e-mail: java-dev-unsubscr...@axis.apache.org For additional commands, e-mail: java-dev-h...@axis.apache.org