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@exoplatform.com">Julien
Viet</a>
* @version $Revision$
*/
+@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 $
*/
+@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<http://www.gnu.org/licenses/>.
+ */
+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(a)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