[exo-jcr-commits] exo-jcr SVN: r2348 - in kernel/trunk/exo.kernel.container/src: main/java/org/exoplatform/container/jmx and 2 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Tue May 4 13:28:48 EDT 2010


Author: nfilotto
Date: 2010-05-04 13:28:47 -0400 (Tue, 04 May 2010)
New Revision: 2348

Added:
   kernel/trunk/exo.kernel.container/src/test/java/org/exoplatform/container/TestExoContainer.java
   kernel/trunk/exo.kernel.container/src/test/resources/org/exoplatform/container/test-exo-container.xml
Modified:
   kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/CachingContainer.java
   kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/jmx/MX4JComponentAdapter.java
Log:
EXOJCR-712: 
1. CachingContainer: Make all the write methods Thread Safe to prevent race conditions and thread safety issues
2. MX4JComponentAdapter: Ensure that only one instance is created per adapter

Modified: kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/CachingContainer.java
===================================================================
--- kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/CachingContainer.java	2010-04-30 18:16:15 UTC (rev 2347)
+++ kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/CachingContainer.java	2010-05-04 17:28:47 UTC (rev 2348)
@@ -37,9 +37,15 @@
  * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
  * @version $Revision$
  */
+ at SuppressWarnings("unchecked")
 public class CachingContainer extends MCIntegrationContainer
 {
 
+   /**
+    * Serial Version UID
+    */
+   private static final long serialVersionUID = 316388590860241305L;
+
    private final ConcurrentMap<Class, ComponentAdapter> adapterByType =
       new ConcurrentHashMap<Class, ComponentAdapter>();
 
@@ -171,67 +177,67 @@
 
    //
 
-   public ComponentAdapter registerComponent(ComponentAdapter componentAdapter)
+   public synchronized ComponentAdapter registerComponent(ComponentAdapter componentAdapter)
       throws DuplicateComponentKeyRegistrationException
    {
       invalidate();
       return super.registerComponent(componentAdapter);
    }
 
-   public ComponentAdapter unregisterComponent(Object componentKey)
+   public synchronized ComponentAdapter unregisterComponent(Object componentKey)
    {
       invalidate();
       return super.unregisterComponent(componentKey);
    }
 
-   public ComponentAdapter registerComponentInstance(Object component) throws PicoRegistrationException
+   public synchronized ComponentAdapter registerComponentInstance(Object component) throws PicoRegistrationException
    {
       invalidate();
       return super.registerComponentInstance(component);
    }
 
-   public ComponentAdapter registerComponentInstance(Object componentKey, Object componentInstance)
+   public synchronized ComponentAdapter registerComponentInstance(Object componentKey, Object componentInstance)
       throws PicoRegistrationException
    {
       invalidate();
       return super.registerComponentInstance(componentKey, componentInstance);
    }
 
-   public ComponentAdapter registerComponentImplementation(Class componentImplementation)
+   public synchronized ComponentAdapter registerComponentImplementation(Class componentImplementation)
       throws PicoRegistrationException
    {
       invalidate();
       return super.registerComponentImplementation(componentImplementation);
    }
 
-   public ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation)
+   public synchronized ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation)
       throws PicoRegistrationException
    {
       invalidate();
       return super.registerComponentImplementation(componentKey, componentImplementation);
    }
 
-   public ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation,
+   public synchronized ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation,
       Parameter[] parameters) throws PicoRegistrationException
    {
       invalidate();
       return super.registerComponentImplementation(componentKey, componentImplementation, parameters);
    }
 
-   public ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation,
+   public synchronized ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation,
       List parameters) throws PicoRegistrationException
    {
       invalidate();
       return super.registerComponentImplementation(componentKey, componentImplementation, parameters);
    }
 
-   public boolean addChildContainer(PicoContainer child)
+   public synchronized boolean addChildContainer(PicoContainer child)
    {
       invalidate();
       return super.addChildContainer(child);
    }
 
-   public boolean removeChildContainer(PicoContainer child)
+   public synchronized boolean removeChildContainer(PicoContainer child)
    {
       invalidate();
       return super.removeChildContainer(child);

Modified: kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/jmx/MX4JComponentAdapter.java
===================================================================
--- kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/jmx/MX4JComponentAdapter.java	2010-04-30 18:16:15 UTC (rev 2347)
+++ kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/jmx/MX4JComponentAdapter.java	2010-05-04 17:28:47 UTC (rev 2348)
@@ -42,10 +42,16 @@
  * @author Benjamin Mestrallet
  * @version $Revision: 1.5 $
  */
+ at SuppressWarnings("unchecked")
 public class MX4JComponentAdapter extends AbstractComponentAdapter
 {
-   private Object instance_;
+   /**
+    * Serial Version ID
+    */
+   private static final long serialVersionUID = -9001193588034229411L;
 
+   private volatile Object instance_;
+
    private Log log = ExoLogger.getLogger("exo.kernel.container.MX4JComponentAdapter");
 
    public MX4JComponentAdapter(Object key, Class implementation)
@@ -58,31 +64,38 @@
       if (instance_ != null)
          return instance_;
 
-      // Get the component
+      //
       ExoContainer exocontainer = (ExoContainer)container;
-      Object key = getComponentKey();
+      Component component = null;
+      ConfigurationManager manager;
       String componentKey;
-      if (key instanceof String)
-         componentKey = (String)key;
-      else
-         componentKey = ((Class)key).getName();
-      ConfigurationManager manager =
-         (ConfigurationManager)exocontainer.getComponentInstanceOfType(ConfigurationManager.class);
-      Component component = manager.getComponent(componentKey);
-
-      //
       try
       {
          InitParams params = null;
          boolean debug = false;
-         if (component != null)
+         synchronized (this)
          {
-            params = component.getInitParams();
-            debug = component.getShowDeployInfo();
+            // Avoid to create duplicate instances if it is called at the same time by several threads
+            if (instance_ != null)
+               return instance_;
+            // Get the component
+            Object key = getComponentKey();
+            if (key instanceof String)
+               componentKey = (String)key;
+            else
+               componentKey = ((Class)key).getName();
+            manager = (ConfigurationManager)exocontainer.getComponentInstanceOfType(ConfigurationManager.class);
+            component = manager.getComponent(componentKey);
+            if (component != null)
+            {
+               params = component.getInitParams();
+               debug = component.getShowDeployInfo();
+            }
+            // Please note that we cannot fully initialize the Object "instance_" before releasing other
+            // threads because it could cause StackOverflowError due to recursive calls
+            instance_ = exocontainer.createComponent(getComponentImplementation(), params);
          }
 
-         instance_ = exocontainer.createComponent(getComponentImplementation(), params);
-
          if (debug)
             log.debug("==> create  component : " + instance_);
          if (component != null && component.getComponentPlugins() != null)
@@ -112,6 +125,7 @@
          }
          throw new RuntimeException(msg, ex);
       }
+
       return instance_;
    }
 

Added: kernel/trunk/exo.kernel.container/src/test/java/org/exoplatform/container/TestExoContainer.java
===================================================================
--- kernel/trunk/exo.kernel.container/src/test/java/org/exoplatform/container/TestExoContainer.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.container/src/test/java/org/exoplatform/container/TestExoContainer.java	2010-05-04 17:28:47 UTC (rev 2348)
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see&lt;http://www.gnu.org/licenses/&gt;.
+ */
+package org.exoplatform.container;
+
+import org.exoplatform.container.component.BaseComponentPlugin;
+import org.exoplatform.container.configuration.ConfigurationManager;
+import org.exoplatform.container.jmx.AbstractTestContainer;
+import org.picocontainer.ComponentAdapter;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.PicoInitializationException;
+import org.picocontainer.PicoIntrospectionException;
+import org.picocontainer.PicoVisitor;
+import org.picocontainer.defaults.DuplicateComponentKeyRegistrationException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto 
+ *          nicolas.filotto at exoplatform.com
+ * 3 mai 2010  
+ */
+public class TestExoContainer extends AbstractTestContainer
+{
+
+   public void testStackOverFlow()
+   {
+      final RootContainer container = createRootContainer("test-exo-container.xml");
+      MyClass value = (MyClass)container.getComponentInstanceOfType(MyClass.class);
+      assertNotNull(value);
+      MyClassPlugin plugin = value.plugin_;
+      assertNotNull(plugin);
+      assertNotNull(plugin.cmanager_);
+      assertEquals(value, plugin.myClass_);
+   }
+   
+   public void testMultiThreading() throws Throwable
+   {
+      final RootContainer container = createRootContainer("test-exo-container.xml");
+      final AtomicReference<MyMTClass> currentMyClass = new AtomicReference<MyMTClass>();
+      testMultiThreading(new Task()
+      {
+
+         public void execute()
+         {
+            MyMTClass value = (MyMTClass)container.getComponentInstance(MyMTClass.class);
+            synchronized (currentMyClass)
+            {
+               if (currentMyClass.get() == null)
+               {
+                  currentMyClass.set(value);
+               }
+            }
+            assertEquals(currentMyClass.get(), container.getComponentInstance(MyMTClass.class));
+         }
+      });      
+      testMultiThreading(new Task()
+      {
+
+         public void execute()
+         {
+            MyMTClass value = (MyMTClass)container.getComponentInstanceOfType(MyMTClass.class);
+            synchronized (currentMyClass)
+            {
+               if (currentMyClass.get() == null)
+               {
+                  currentMyClass.set(value);
+               }
+            }
+            assertEquals(currentMyClass.get(), container.getComponentInstanceOfType(MyMTClass.class));
+         }
+      });
+      final AtomicReference<ComponentAdapter> ar = new AtomicReference<ComponentAdapter>();
+      testMultiThreading(new Task()
+      {
+         public void execute()
+         {
+            try
+            {
+               ComponentAdapter ca = new ComponentAdapter()
+               {
+
+                  public void accept(PicoVisitor paramPicoVisitor)
+                  {
+                  }
+
+                  public Class getComponentImplementation()
+                  {
+                     return MyClass.class;
+                  }
+
+                  public Object getComponentInstance(PicoContainer paramPicoContainer)
+                     throws PicoInitializationException, PicoIntrospectionException
+                  {
+                     return new MyClass();
+                  }
+
+                  public Object getComponentKey()
+                  {
+                     return "a";
+                  }
+
+                  public void verify(PicoContainer paramPicoContainer) throws PicoIntrospectionException
+                  {
+                  }
+
+               };
+               ar.set(container.registerComponent(ca));
+            }
+            catch (DuplicateComponentKeyRegistrationException e)
+            {
+               // ignore expected behavior
+            }
+         }
+      });
+      testMultiThreading(new Task()
+      {
+
+         public void execute()
+         {
+            assertEquals(ar.get(), container.getComponentAdapter("a"));
+         }
+      });
+      testMultiThreading(new Task()
+      {
+
+         public void execute()
+         {
+            container.unregisterComponent("a");
+         }
+      });
+
+   }
+
+   private void testMultiThreading(final Task task) throws Throwable
+   {
+      int threads = 50;
+      final CountDownLatch startSignal = new CountDownLatch(1);
+      final CountDownLatch doneSignal = new CountDownLatch(threads);
+      final List<Throwable> errors = Collections.synchronizedList(new ArrayList<Throwable>());
+      for (int i = 0; i < threads; i++)
+      {
+         Thread thread = new Thread()
+         {
+            public void run()
+            {
+               try
+               {
+                  startSignal.await();
+                  task.execute();
+               }
+               catch (Throwable e)
+               {
+                  errors.add(e);
+               }
+               finally
+               {
+                  doneSignal.countDown();
+               }
+            }
+         };
+         thread.start();
+      }
+      startSignal.countDown();
+      doneSignal.await();
+      if (!errors.isEmpty())
+      {
+         for (Throwable e : errors)
+         {
+            e.printStackTrace();
+         }
+         throw errors.get(0);
+      }
+   }
+
+   public interface Task
+   {
+      public void execute();
+   }
+   
+   public static class MyMTClass
+   {
+      public MyMTClass() throws InterruptedException
+      {
+         // Make the thread wait to ensure that the thread safety issue is properly solved
+         Thread.sleep(10);
+      }
+   }
+
+   public static class MyClass
+   {
+      public MyClassPlugin plugin_;
+      public void add(MyClassPlugin plugin)
+      {
+         this.plugin_ = plugin;
+      }
+   }
+   
+   public static class MyClassPlugin extends BaseComponentPlugin
+   {
+      public ConfigurationManager cmanager_;
+      public MyClass myClass_;
+      public MyClassPlugin(ConfigurationManager cmanager, MyClass myClass)
+      {
+         this.cmanager_ = cmanager;
+         this.myClass_ = myClass;
+      }
+   }
+}
\ No newline at end of file

Added: kernel/trunk/exo.kernel.container/src/test/resources/org/exoplatform/container/test-exo-container.xml
===================================================================
--- kernel/trunk/exo.kernel.container/src/test/resources/org/exoplatform/container/test-exo-container.xml	                        (rev 0)
+++ kernel/trunk/exo.kernel.container/src/test/resources/org/exoplatform/container/test-exo-container.xml	2010-05-04 17:28:47 UTC (rev 2348)
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+	<!--
+
+		Copyright (C) 2009 eXo Platform SAS. This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
+		as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the
+		hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+		Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not,
+		write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+	-->
+<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_1.xsd http://www.exoplaform.org/xml/ns/kernel_1_1.xsd"
+	xmlns="http://www.exoplaform.org/xml/ns/kernel_1_1.xsd">
+	<component>
+		<type>org.exoplatform.container.TestExoContainer$MyMTClass</type>
+	</component>
+	<component>
+		<type>org.exoplatform.container.TestExoContainer$MyClass</type>
+		<component-plugins>
+			<component-plugin>
+				<name>stackoverflow-test-plugin</name>
+				<set-method>add</set-method>
+				<type>org.exoplatform.container.TestExoContainer$MyClassPlugin</type>
+			</component-plugin>
+		</component-plugins>
+	</component>
+</configuration>
\ No newline at end of file



More information about the exo-jcr-commits mailing list