From 4b7ba20aae3680c976495a864115cb2a9fc05111 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 7 Aug 2019 10:44:04 +0200 Subject: [PATCH] Fixes #3929 - Deadlock between new HTTP2Connection() and Server.stop(). Updated code after review. Now a managed failed bean is restarted if its container is restarted. Added more test cases. Signed-off-by: Simone Bordet --- .../util/component/ContainerLifeCycle.java | 2 +- .../component/ContainerLifeCycleTest.java | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java index a17cf87e3a8..78cda3e98ab 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -106,7 +106,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, switch (b._managed) { case MANAGED: - if (l.isStopped()) + if (l.isStopped() || l.isFailed()) start(l); break; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java index fce27f598d5..c4458451e27 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.util.TypeUtil; @@ -675,4 +676,71 @@ public class ContainerLifeCycleTest longLived.start(); longLived.stop(); } + + @Test + public void testFailedManagedBeanCanBeRestarted() throws Exception + { + AtomicBoolean fail = new AtomicBoolean(); + ContainerLifeCycle container = new ContainerLifeCycle(); + ContainerLifeCycle bean1 = new ContainerLifeCycle(); + ContainerLifeCycle bean2 = new ContainerLifeCycle() + { + @Override + protected void doStart() throws Exception + { + super.doStart(); + // Fail only the first time. + if (fail.compareAndSet(false, true)) + throw new RuntimeException(); + } + }; + ContainerLifeCycle bean3 = new ContainerLifeCycle(); + container.addBean(bean1); + container.addBean(bean2); + container.addBean(bean3); + + // Start the first time, it should fail. + assertThrows(RuntimeException.class, container::start); + assertTrue(container.isFailed()); + assertTrue(bean1.isStopped()); + assertTrue(bean2.isFailed()); + assertTrue(bean3.isStopped()); + + // Re-start, it should succeed. + container.start(); + assertTrue(container.isStarted()); + assertTrue(bean1.isStarted()); + assertTrue(bean2.isStarted()); + assertTrue(bean3.isStarted()); + } + + @Test + public void testFailedAutoBeanIsNotRestarted() throws Exception + { + AtomicBoolean fail = new AtomicBoolean(); + ContainerLifeCycle bean = new ContainerLifeCycle() + { + @Override + protected void doStart() throws Exception + { + super.doStart(); + // Fail only the first time. + if (fail.compareAndSet(false, true)) + throw new RuntimeException(); + } + }; + // The bean is started externally and fails. + assertThrows(RuntimeException.class, bean::start); + + // The same bean now becomes part of a container. + ContainerLifeCycle container = new ContainerLifeCycle(); + container.addBean(bean); + assertTrue(container.isAuto(bean)); + + // Start the container, the bean must not be managed. + container.start(); + assertTrue(container.isStarted()); + assertTrue(bean.isFailed()); + assertTrue(container.isUnmanaged(bean)); + } }