Author: nfilotto
Date: 2011-02-25 15:24:27 -0500 (Fri, 25 Feb 2011)
New Revision: 4021
Added:
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/LifecycleVisitor.java
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/proccess/WorkerService.java
jcr/trunk/exo.jcr.component.ftp/src/main/java/org/exoplatform/services/ftp/FtpServerImpl.java
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ConcurrentPicoContainer.java
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ExoContainer.java
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java
kernel/trunk/exo.kernel.container/src/test/java/org/exoplatform/container/TestExoContainer.java
Log:
EXOJCR-1213: Thanks to this patch if an error occurs in the stop or dispose methods, the
error will be logged with the debug level, such that we are sure that the stop and dispose
methods will be called on all components. This patch also stops the RootContainer on
context destroy of the starter.war, it is necessary since the shutdown hooks are not
triggered on tomcat stop command. And finally this patch convert the FtpServer and the
WorkerService into daemon to avoid blocking the JVM to stop normally.
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/proccess/WorkerService.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/proccess/WorkerService.java 2011-02-25
14:56:08 UTC (rev 4020)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/proccess/WorkerService.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -79,7 +79,7 @@
*/
public WorkerService(int threadCount)
{
- executor = new ScheduledThreadPoolExecutor(threadCount);
+ this(threadCount, "pool");
}
/**
@@ -90,7 +90,7 @@
*/
public WorkerService(int threadCount, String threadNamePrefix)
{
- executor = new ScheduledThreadPoolExecutor(threadCount, new
WorkerThreadFactory(threadNamePrefix));
+ this(threadCount, threadNamePrefix, true);
}
/**
Modified:
jcr/trunk/exo.jcr.component.ftp/src/main/java/org/exoplatform/services/ftp/FtpServerImpl.java
===================================================================
---
jcr/trunk/exo.jcr.component.ftp/src/main/java/org/exoplatform/services/ftp/FtpServerImpl.java 2011-02-25
14:56:08 UTC (rev 4020)
+++
jcr/trunk/exo.jcr.component.ftp/src/main/java/org/exoplatform/services/ftp/FtpServerImpl.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -219,6 +219,7 @@
{
this.ftpServer = ftpServer;
this.serverSocket = serverSocket;
+ setDaemon(true);
}
public void disable()
Modified:
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ConcurrentPicoContainer.java
===================================================================
---
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ConcurrentPicoContainer.java 2011-02-25
14:56:08 UTC (rev 4020)
+++
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ConcurrentPicoContainer.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -35,7 +35,6 @@
import org.picocontainer.defaults.DefaultPicoContainer;
import org.picocontainer.defaults.DuplicateComponentKeyRegistrationException;
import org.picocontainer.defaults.InstanceComponentAdapter;
-import org.picocontainer.defaults.LifecycleVisitor;
import org.picocontainer.defaults.VerifyingVisitor;
import java.io.Serializable;
@@ -460,6 +459,30 @@
}
/**
+ * Indicates whether or not the container can be started
+ */
+ protected boolean canBeStarted()
+ {
+ return !disposed.get() && !started.get();
+ }
+
+ /**
+ * Indicates whether or not the container can be stopped
+ */
+ protected boolean canBeStopped()
+ {
+ return !disposed.get() && started.get();
+ }
+
+ /**
+ * Indicates whether or not the container can be disposed
+ */
+ protected boolean canBeDisposed()
+ {
+ return !disposed.get();
+ }
+
+ /**
* Start the components of this PicoContainer and all its logical child containers.
* Any component implementing the lifecycle interface {@link
org.picocontainer.Startable} will be started.
* @see #makeChildContainer()
@@ -468,7 +491,7 @@
*/
public void start()
{
- if (disposed.get() || started.get())
+ if (!canBeStarted())
return;
LifecycleVisitor.start(this);
started.set(true);
@@ -483,7 +506,7 @@
*/
public void stop()
{
- if (disposed.get() || !started.get())
+ if (!canBeStopped())
return;
LifecycleVisitor.stop(this);
started.set(false);
@@ -498,7 +521,7 @@
*/
public void dispose()
{
- if (disposed.get())
+ if (!canBeDisposed())
return;
LifecycleVisitor.dispose(this);
disposed.set(true);
Modified:
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ExoContainer.java
===================================================================
---
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ExoContainer.java 2011-02-25
14:56:08 UTC (rev 4020)
+++
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/ExoContainer.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -188,21 +188,24 @@
}
@Override
- public void dispose()
+ public synchronized void dispose()
{
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(ContainerPermissions.MANAGE_CONTAINER_PERMISSION);
- destroyContainerInternal();
- super.dispose();
+ if (canBeDisposed())
+ {
+ destroyContainerInternal();
+ super.dispose();
+ }
}
/**
* Starts the container
* @param init indicates if the container must be initialized first
*/
- public void start(boolean init)
+ public synchronized void start(boolean init)
{
SecurityManager security = System.getSecurityManager();
if (security != null)
@@ -217,24 +220,31 @@
}
@Override
- public void start()
+ public synchronized void start()
{
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(ContainerPermissions.MANAGE_CONTAINER_PERMISSION);
- super.start();
- startContainerInternal();
+
+ if (canBeStarted())
+ {
+ super.start();
+ startContainerInternal();
+ }
}
@Override
- public void stop()
+ public synchronized void stop()
{
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(ContainerPermissions.MANAGE_CONTAINER_PERMISSION);
- stopContainerInternal();
- super.stop();
+ if (canBeStopped())
+ {
+ stopContainerInternal();
+ super.stop();
+ }
}
/**
Added:
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/LifecycleVisitor.java
===================================================================
---
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/LifecycleVisitor.java
(rev 0)
+++
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/LifecycleVisitor.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+package org.exoplatform.container;
+
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.picocontainer.ComponentAdapter;
+import org.picocontainer.Disposable;
+import org.picocontainer.Parameter;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.PicoIntrospectionException;
+import org.picocontainer.Startable;
+import org.picocontainer.defaults.AbstractPicoVisitor;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:nicolas.filotto@exoplatform.com">Nicolas
Filotto</a>
+ * @version $Id$
+ */
+public class LifecycleVisitor extends AbstractPicoVisitor
+{
+
+ private static final Log LOG =
ExoLogger.getLogger("exo.kernel.container.LifecycleVisitor");
+
+ private static final Method START;
+
+ private static final Method STOP;
+
+ private static final Method DISPOSE;
+ static
+ {
+ try
+ {
+ START = Startable.class.getMethod("start", (Class<?>[])null);
+ STOP = Startable.class.getMethod("stop", (Class<?>[])null);
+ DISPOSE = Disposable.class.getMethod("dispose",
(Class<?>[])null);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new InternalError(e.getMessage());
+ }
+ }
+
+ private final Method method;
+
+ private final Class<?> type;
+
+ private final boolean visitInInstantiationOrder;
+
+ private final List componentInstances;
+
+ private final boolean ignoreError;
+
+ public LifecycleVisitor(Method method, Class<?> ofType, boolean
visitInInstantiationOrder, boolean ignoreError)
+ {
+ this.method = method;
+ this.type = ofType;
+ this.visitInInstantiationOrder = visitInInstantiationOrder;
+ this.componentInstances = new ArrayList();
+ this.ignoreError = ignoreError;
+ }
+
+ public Object traverse(Object node)
+ {
+ componentInstances.clear();
+ try
+ {
+ super.traverse(node);
+ if (!visitInInstantiationOrder)
+ {
+ Collections.reverse(componentInstances);
+ }
+ for (Iterator iterator = componentInstances.iterator(); iterator.hasNext();)
+ {
+ Object o = iterator.next();
+ try
+ {
+ method.invoke(o, (Object[])null);
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (ignoreError)
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Can't call " + method.getName() + " on
" + o, e);
+ }
+ continue;
+ }
+ throw new PicoIntrospectionException("Can't call " +
method.getName() + " on " + o, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ if (ignoreError)
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Can't call " + method.getName() + " on
" + o, e);
+ }
+ continue;
+ }
+ throw new PicoIntrospectionException("Can't call " +
method.getName() + " on " + o, e);
+ }
+ catch (InvocationTargetException e)
+ {
+ if (ignoreError)
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Failed when calling " + method.getName() +
" on " + o, e.getTargetException());
+ }
+ continue;
+ }
+ throw new PicoIntrospectionException("Failed when calling " +
method.getName() + " on " + o,
+ e.getTargetException());
+ }
+ }
+ }
+ finally
+ {
+ componentInstances.clear();
+ }
+ return Void.TYPE;
+ }
+
+ public void visitContainer(PicoContainer pico)
+ {
+ checkTraversal();
+ componentInstances.addAll(pico.getComponentInstancesOfType(type));
+ }
+
+ public void visitComponentAdapter(ComponentAdapter componentAdapter)
+ {
+ checkTraversal();
+ }
+
+ public void visitParameter(Parameter parameter)
+ {
+ checkTraversal();
+ }
+
+ /**
+ * Invoke the standard PicoContainer lifecycle for {@link Startable#start()}.
+ * @param node The node to start the traversal.
+ */
+ public static void start(Object node)
+ {
+ new LifecycleVisitor(START, Startable.class, true, false).traverse(node);;
+ }
+
+ /**
+ * Invoke the standard PicoContainer lifecycle for {@link Startable#stop()}.
+ * @param node The node to start the traversal.
+ */
+ public static void stop(Object node)
+ {
+ new LifecycleVisitor(STOP, Startable.class, false, true).traverse(node);;
+ }
+
+ /**
+ * Invoke the standard PicoContainer lifecycle for {@link Disposable#dispose()}.
+ * @param node The node to start the traversal.
+ */
+ public static void dispose(Object node)
+ {
+ new LifecycleVisitor(DISPOSE, Disposable.class, false, true).traverse(node);;
+ }
+
+}
Modified:
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java
===================================================================
---
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java 2011-02-25
14:56:08 UTC (rev 4020)
+++
kernel/trunk/exo.kernel.container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -45,6 +45,16 @@
*/
public void contextDestroyed(ServletContextEvent event)
{
+ SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
+ {
+ public Void run()
+ {
+ // Ensure that the root container is stopped properly since the shutdown
hook
+ // doesn't work in some cases for example with tomcat when we call the
stop command
+ RootContainer.getInstance().stop();
+ return null;
+ }
+ });
}
/**
Modified:
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 2011-02-25
14:56:08 UTC (rev 4020)
+++
kernel/trunk/exo.kernel.container/src/test/java/org/exoplatform/container/TestExoContainer.java 2011-02-25
20:24:27 UTC (rev 4021)
@@ -22,6 +22,7 @@
import org.exoplatform.container.support.ContainerBuilder;
import org.exoplatform.container.xml.InitParams;
import org.picocontainer.ComponentAdapter;
+import org.picocontainer.Disposable;
import org.picocontainer.PicoContainer;
import org.picocontainer.PicoInitializationException;
import org.picocontainer.PicoIntrospectionException;
@@ -669,5 +670,170 @@
public void stop()
{
}
- }
+ }
+
+ public void testLifeCycle() throws Throwable
+ {
+ ConcurrentPicoContainer container = new ConcurrentPicoContainer();
+ assertTrue(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertTrue(container.canBeDisposed());
+ container.registerComponentImplementation(LC1.class);
+ container.registerComponentImplementation(LC2.class);
+ container.registerComponentImplementation(LC3.class);
+ container.registerComponentImplementation(LC4.class);
+ container.registerComponentImplementation(LC5.class);
+ try
+ {
+ container.start();
+ fail("Should fail due to the start method of C1");
+ }
+ catch (Exception e)
+ {
+ // igonre me
+ }
+ LC1 c1 = (LC1)container.getComponentInstanceOfType(LC1.class);
+ LC2 c2 = (LC2)container.getComponentInstanceOfType(LC2.class);
+ LC3 c3 = (LC3)container.getComponentInstanceOfType(LC3.class);
+ LC4 c4 = (LC4)container.getComponentInstanceOfType(LC4.class);
+ LC5 c5 = (LC5)container.getComponentInstanceOfType(LC5.class);
+ assertFalse(c2.started && c3.started && c4.started);
+ assertTrue(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertTrue(container.canBeDisposed());
+ container.stop();
+ assertTrue(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertTrue(container.canBeDisposed());
+ container.dispose();
+ assertTrue(c1.disposed && c2.disposed && c5.disposed);
+ assertFalse(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertFalse(container.canBeDisposed());
+ container = new ConcurrentPicoContainer();
+ assertTrue(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertTrue(container.canBeDisposed());
+ container.registerComponentImplementation(LC2.class);
+ container.registerComponentImplementation(LC3.class);
+ container.registerComponentImplementation(LC4.class);
+ container.registerComponentImplementation(LC5.class);
+ container.start();
+ c2 = (LC2)container.getComponentInstanceOfType(LC2.class);
+ c3 = (LC3)container.getComponentInstanceOfType(LC3.class);
+ c4 = (LC4)container.getComponentInstanceOfType(LC4.class);
+ assertTrue(c2.started && c3.started && c4.started);
+ assertFalse(container.canBeStarted());
+ assertTrue(container.canBeStopped());
+ assertTrue(container.canBeDisposed());
+ container.stop();
+ assertTrue(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertTrue(container.canBeDisposed());
+ container.dispose();
+ assertTrue(c1.disposed && c2.disposed && c5.disposed);
+ assertFalse(container.canBeStarted());
+ assertFalse(container.canBeStopped());
+ assertFalse(container.canBeDisposed());
+ }
+
+ public static class LC1 implements Startable, Disposable
+ {
+
+ public boolean started;
+ public boolean stopped;
+ public boolean disposed;
+
+ public void start()
+ {
+ throw new RuntimeException();
+ }
+
+ public void stop()
+ {
+ stopped = true;
+ }
+
+ public void dispose()
+ {
+ disposed = true;
+ }
+ }
+
+ public static class LC2 implements Startable, Disposable
+ {
+
+ public boolean started;
+ public boolean stopped;
+ public boolean disposed;
+
+ public void start()
+ {
+ started = true;
+ }
+
+ public void stop()
+ {
+ throw new RuntimeException();
+ }
+
+ public void dispose()
+ {
+ disposed = true;
+ }
+ }
+
+ public static class LC3 implements Startable, Disposable
+ {
+
+ public boolean started;
+ public boolean stopped;
+ public boolean disposed;
+
+ public void start()
+ {
+ started = true;
+ }
+
+ public void stop()
+ {
+ stopped = true;
+ }
+
+ public void dispose()
+ {
+ throw new RuntimeException();
+ }
+ }
+
+ public static class LC4 implements Startable
+ {
+
+ public boolean started;
+ public boolean stopped;
+ public boolean disposed;
+
+ public void start()
+ {
+ started = true;
+ }
+
+ public void stop()
+ {
+ stopped = true;
+ }
+ }
+
+ public static class LC5 implements Disposable
+ {
+
+ public boolean started;
+ public boolean stopped;
+ public boolean disposed;
+
+ public void dispose()
+ {
+ disposed = true;
+ }
+ }
}