3 Replies Latest reply on May 31, 2011 4:09 PM by pediddle

    Seam 2.1.1.GA with Spring new javassist class created for each new Component instance

    ncadell.seam.thebap.org
      Hi,

      We are using Seam 2.1.1.GA with Spring. We are having an issue where it looks like a new javassist class is being created each time a Seam component is created.

      Normally the org.jboss.seam.Component caches the Class<ProxyObject> in its private 'factory' field. Each time a new instance of the component is required this cached class is used to create the instance.

      When using Spring the Component is actually a org.jboss.seam.ioc.spring.SpringComponent which extends the org.jboss.seam.ioc.IoCComponent. The SpringComponent asks Spring for the instance and the IoCComponent wraps this in a javassist wrapper.

      The issue that we are seeing is that the IoCComponent, in its instantiateJavaBean() method, uses ProxyUtils.enhance() to wrap the bean in javassist proxy, but does not, or is unable to, cache the Class<ProxyObject> created in the Component's 'factory' field. Therefore each time ProxyUtils.enhance() is called it creates a new class with a new name of the form XXX_$$_javassist_XXX using an incrementing counter. These generated classes are never cleaned up by the JVM and cause a slow memory leak.

      Have I got this wrong? Is this a known issue? How can we work around it?

      Many thanks,
      Nicko
        • 1. Re: Seam 2.1.1.GA with Spring new javassist class created for each new Component instance
          ncadell.seam.thebap.org
          The solution to this seems to be to patch the IoCComponent so that it caches the Class<ProxyObject> as follows:

          public abstract class IoCComponent extends Component
          {
               protected final LogProvider log = Logging.getLogProvider(getClass());

               /**
                * Creates a Seam Component from other IoC container
                *
                * @param clazz class
                * @param name  component name
                * @param scope component scope
                */
               public IoCComponent(Class clazz, String name, ScopeType scope)
               {
                    super(clazz, name, scope, false, new String[0], null);
               }

               protected abstract String getIoCName();

               protected abstract Object instantiateIoCBean() throws Exception;

               /**
                * Instantiates a IoC bean and provides it as a
                * java bean to be wrapped by Seam.
                *
                * @see org.jboss.seam.Component#instantiateJavaBean()
                */
               @Override
               protected Object instantiateJavaBean() throws Exception
               {
                    Object bean = instantiateIoCBean();
                    // initialize the bean following Component.instantiateJavaBean()'s pattern.
                    if (!isInterceptionEnabled())
                    {
                         initialize(bean);
                         // Only call postConstruct if the bean is not stateless otherwise in the case of a singleton it wowuld be
                         // called every time seam request the bean not just when it is created.
                         if (getScope() != ScopeType.STATELESS)
                         {
                              callPostConstructMethod(bean);
                         }
                    }
                    else if (!(bean instanceof Proxy))
                    {
                         // Add all of the interfaces of the bean instance into the Seam
                         // proxy bean because spring's proxies add a bunch of interfaces too
                         // that should be accessible.
                         Set<Class> interfaces = new HashSet<Class>(Arrays.asList(bean.getClass().getInterfaces()));
                         interfaces.add(HttpSessionActivationListener.class);
                         interfaces.add(Mutable.class);
                         interfaces.add(Proxy.class);
                         // enhance bean
                         bean = proxyUtilsEnhance(bean, interfaces, this); // Changed to call enahance that will cache the class
                    }
                    return bean;
               }

               // Copied from ProxyUtils
               public Object proxyUtilsEnhance(final Object bean, final Set<Class> interfaces, final IoCComponent component) throws Exception
               {
                    Class beanClass = bean.getClass();
                    if (ProxyUtils.isProxy(beanClass))
                    {
                         throw new RuntimeException("Seam cannot wrap JDK proxied IoC beans. Please use CGLib or Javassist proxying instead");
                    }
                    //
                    if (ProxyUtils.isCglibProxyClass(beanClass) || ProxyUtils.isJavassistProxyClass(beanClass))
                    {
                         beanClass = beanClass.getSuperclass();
                    }

                    if (log.isDebugEnabled())
                    {
                         log.debug("Creating proxy for " + component.getIoCName() + " Seam component '" + component.getName() + "' using class: " + beanClass.getName());
                    }

                    // create pojo proxy
                    final JavaBeanInterceptor interceptor = new JavaBeanInterceptor(bean, component);
                    // Should probably create a Factory but required a lot of duplicated
                    // code and there is potential for a spring bean to provide
                    // different interfaces at different times in an application. If
                    // need is great I can create a Factory and assume the same
                    // interfaces all the time.
                    final ProxyObject po = getIocProxyFactory(beanClass, interfaces).newInstance(); // Changed to get cached class
                    po.setHandler(interceptor);
                    interceptor.postConstruct();
                    return po;
               }

               private Class<ProxyObject> iocFactory;

               private synchronized Class<ProxyObject> getIocProxyFactory(final Class beanClass, Collection<Class> businessInterfaces)
               {
                    if (iocFactory == null)
                    {
                         iocFactory = createProxyFactory(ComponentType.JAVA_BEAN, beanClass, businessInterfaces);
                    }
                    return iocFactory;
               }
          }

          • 2. Re: Seam 2.1.1.GA with Spring new javassist class created for each new Component instance
            andy2010
            Thanks man! You saved me a lot of work not having to reinvent your solution after days of troubleshooting!
            Our Seam Jboss Spring hibernate server ended up with this
            java.lang.OutOfMemoryError: PermGen space
            We noticed thousands of these JavaAssist classes (ending with _$$_javassist_)  being created but never removed.
            Thank you for publishing this solution and making the world move forward!
            /Andy