Skip to content

Commit fc54354

Browse files
authored
TOMEE-4631 - arquillian-tomee-remote: re-register container on every Manager bootstrap (#2817)
RemoteTomEEExtension guarded container registration behind a JVM-static AtomicBoolean latch. Arquillian instantiates the extension and calls register() once per Manager bootstrap, so the latch let only the first Manager in a JVM fork register its DeployableContainer. Any second Manager in the same fork (e.g. maven-failsafe-plugin's rerunFailingTestsCount) got an empty ContainerRegistry and failed every deployment with "DeploymentScenario contains a target (_DEFAULT_) not matching any defined Container in the registry". Drop the static latch (and the static lock that only guarded it) and register unconditionally, matching EmbeddedTomEEExtension. The existing try/catch(IllegalArgumentException) remains as the guard against a genuine double-registration within a single builder. Adds RemoteTomEEExtensionReRegistrationTest covering re-registration across two Manager bootstraps in the same JVM.
1 parent f98ba65 commit fc54354

2 files changed

Lines changed: 126 additions & 23 deletions

File tree

arquillian/arquillian-tomee-remote/src/main/java/org/apache/tomee/arquillian/remote/RemoteTomEEExtension.java

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,40 +26,28 @@
2626
import org.jboss.arquillian.core.spi.LoadableExtension;
2727
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
2828

29-
import java.util.concurrent.atomic.AtomicBoolean;
30-
import java.util.concurrent.locks.ReentrantLock;
3129
import java.util.logging.Level;
3230
import java.util.logging.Logger;
3331

3432
public class RemoteTomEEExtension implements LoadableExtension {
3533

3634
private static final String ADAPTER = "tomee-remote";
37-
private static final AtomicBoolean registered = new AtomicBoolean(false);
38-
private static final ReentrantLock lock = new ReentrantLock();
3935

4036
@Override
4137
public void register(final ExtensionBuilder builder) {
4238
if (ArquillianUtil.isCurrentAdapter(ADAPTER)) {
43-
44-
final ReentrantLock l = lock;
45-
l.lock();
46-
39+
// Register unconditionally (like EmbeddedTomEEExtension): register() runs once per
40+
// Manager bootstrap, so a JVM-static latch skipped every Manager after the first in
41+
// the same fork and broke failsafe reruns (TOMEE-4631). The try/catch below guards
42+
// against a genuine double-registration within a single builder.
4743
try {
48-
49-
if (!registered.getAndSet(true)) {
50-
51-
try {
52-
builder.observer(RemoteInitialContextObserver.class);
53-
builder.observer(DeploymentExceptionObserver.class);
54-
builder.service(DeployableContainer.class, RemoteTomEEContainer.class)
55-
.service(AuxiliaryArchiveAppender.class, RemoteTomEEEJBEnricherArchiveAppender.class)
56-
.service(ResourceProvider.class, DeploymentExceptionProvider.class);
57-
} catch (final IllegalArgumentException e) {
58-
Logger.getLogger(RemoteTomEEExtension.class.getName()).log(Level.WARNING, "RemoteTomEEExtension: " + e.getMessage());
59-
}
60-
}
61-
} finally {
62-
l.unlock();
44+
builder.observer(RemoteInitialContextObserver.class);
45+
builder.observer(DeploymentExceptionObserver.class);
46+
builder.service(DeployableContainer.class, RemoteTomEEContainer.class)
47+
.service(AuxiliaryArchiveAppender.class, RemoteTomEEEJBEnricherArchiveAppender.class)
48+
.service(ResourceProvider.class, DeploymentExceptionProvider.class);
49+
} catch (final IllegalArgumentException e) {
50+
Logger.getLogger(RemoteTomEEExtension.class.getName()).log(Level.WARNING, "RemoteTomEEExtension: " + e.getMessage());
6351
}
6452
}
6553
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.tomee.arquillian.remote;
19+
20+
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
21+
import org.jboss.arquillian.core.spi.LoadableExtension;
22+
import org.jboss.arquillian.core.spi.LoadableExtension.ExtensionBuilder;
23+
import org.jboss.arquillian.core.spi.context.Context;
24+
import org.junit.Test;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
import static org.junit.Assert.assertTrue;
30+
31+
/**
32+
* Reproducer for TOMEE-4631.
33+
*
34+
* <p>{@link RemoteTomEEExtension} guards container registration behind a JVM-static
35+
* {@code AtomicBoolean} latch. {@link LoadableExtension#register(ExtensionBuilder)} is invoked
36+
* once per Arquillian {@code Manager} bootstrap. When a second {@code Manager} is bootstrapped in
37+
* the same JVM fork (the classic trigger being maven-failsafe-plugin's
38+
* {@code rerunFailingTestsCount}, which spins up a fresh {@code Manager} to rerun a failed test),
39+
* the static flag is already {@code true}, so the second bootstrap skips
40+
* {@code builder.service(DeployableContainer.class, ...)} and that {@code Manager} ends up with an
41+
* empty {@code ContainerRegistry}. Deployments then fail with:</p>
42+
*
43+
* <pre>
44+
* org.jboss.arquillian.container.test.impl.client.deployment.ValidationException:
45+
* DeploymentScenario contains a target (_DEFAULT_) not matching any defined Container in the
46+
* registry. Please include at least 1 Deployable Container on your Classpath.
47+
* </pre>
48+
*
49+
* <p>The sibling {@code EmbeddedTomEEExtension} has no such latch and registers unconditionally,
50+
* which is why every other Arquillian adapter survives a rerun and only arquillian-tomee-remote
51+
* does not.</p>
52+
*
53+
* <p>This test drives {@code register(...)} the same way Arquillian's {@code LoadableExtensionLoader}
54+
* does (once per simulated {@code Manager} bootstrap, each with its own {@link ExtensionBuilder})
55+
* and asserts the {@link DeployableContainer} is registered for every bootstrap, not just the
56+
* first. It failed on the former static-latch implementation and guards the unconditional
57+
* registration that replaced it.</p>
58+
*/
59+
public class RemoteTomEEExtensionReRegistrationTest {
60+
61+
@Test
62+
public void containerIsReRegisteredOnEveryManagerBootstrap() {
63+
// 1st Manager bootstrap (e.g. the initial failsafe run)
64+
final RecordingExtensionBuilder first = new RecordingExtensionBuilder();
65+
new RemoteTomEEExtension().register(first);
66+
assertTrue("Sanity: the first Manager bootstrap must register the DeployableContainer",
67+
first.registeredDeployableContainer());
68+
69+
// 2nd Manager bootstrap in the same JVM (e.g. a failsafe rerunFailingTestsCount rerun)
70+
final RecordingExtensionBuilder second = new RecordingExtensionBuilder();
71+
new RemoteTomEEExtension().register(second);
72+
73+
// TOMEE-4631: the static latch makes the second bootstrap skip the DeployableContainer
74+
// service, leaving the second Manager's ContainerRegistry empty -> _DEFAULT_ matches nothing.
75+
assertTrue("TOMEE-4631: a second Arquillian Manager in the same JVM did NOT get a "
76+
+ "DeployableContainer registered; reruns (e.g. failsafe rerunFailingTestsCount) "
77+
+ "will fail with 'DeploymentScenario contains a target (_DEFAULT_) not matching "
78+
+ "any defined Container in the registry'.",
79+
second.registeredDeployableContainer());
80+
}
81+
82+
/**
83+
* Minimal {@link ExtensionBuilder} that records which service contracts were registered,
84+
* standing in for the real builder backed by Arquillian's {@code ServiceRegistry}.
85+
*/
86+
private static final class RecordingExtensionBuilder implements ExtensionBuilder {
87+
private final List<Class<?>> services = new ArrayList<>();
88+
89+
@Override
90+
public <T> ExtensionBuilder service(final Class<T> service, final Class<? extends T> impl) {
91+
services.add(service);
92+
return this;
93+
}
94+
95+
@Override
96+
public <T> ExtensionBuilder override(final Class<T> service, final Class<? extends T> oldImpl,
97+
final Class<? extends T> newImpl) {
98+
return this;
99+
}
100+
101+
@Override
102+
public ExtensionBuilder observer(final Class<?> observer) {
103+
return this;
104+
}
105+
106+
@Override
107+
public ExtensionBuilder context(final Class<? extends Context> context) {
108+
return this;
109+
}
110+
111+
boolean registeredDeployableContainer() {
112+
return services.contains(DeployableContainer.class);
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)