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 <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2019-08-07 10:44:04 +02:00
parent f484b83c2e
commit 4b7ba20aae
2 changed files with 69 additions and 1 deletions

View File

@ -106,7 +106,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
switch (b._managed) switch (b._managed)
{ {
case MANAGED: case MANAGED:
if (l.isStopped()) if (l.isStopped() || l.isFailed())
start(l); start(l);
break; break;

View File

@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
@ -675,4 +676,71 @@ public class ContainerLifeCycleTest
longLived.start(); longLived.start();
longLived.stop(); 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));
}
} }