Skip to content

Commit b40c392

Browse files
fix: prevent startup failures on non-Jakarta EE compliant environments (#477)
* fix: prevent startup failures on non-Jakarta EE compliant environments This change provides a solution for environments that aren't fully Jakarta EE compliant (e.g., Tomcat with Weld) by implementing a runtime check for ManagedExecutorService availability. This prevents deployment failures by: 1. Programmatically resolving the bean through CDI 2. Gracefully handling missing ManagedExecutorService in the classpath 3. Handling javax.naming.NameNotFoundException when JNDI lookup fails The implementation allows for fallback to alternative execution strategies when necessary, improving application resilience. Also adds tests for non-CDI compliant environment scenarios. Fixes #476 * fix test --------- Co-authored-by: Mikhail Shabarov <61410877+mshabarov@users.noreply.github.com>
1 parent f77b691 commit b40c392

File tree

12 files changed

+459
-48
lines changed

12 files changed

+459
-48
lines changed

vaadin-cdi-itest/pom.xml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<maven.deploy.skip>true</maven.deploy.skip>
1616
<surefire.groups></surefire.groups>
1717
<surefire.excludedGroups>com.vaadin.cdi.itest.SlowTests</surefire.excludedGroups>
18+
<apache-tomcat.version>11.0.8</apache-tomcat.version>
1819
<apache-tomee.version>10.1.0</apache-tomee.version>
1920
<wildfly.version>36.0.1.Final</wildfly.version>
2021
<openliberty.version>25.0.0.6</openliberty.version>
@@ -379,6 +380,94 @@
379380
</plugins>
380381
</build>
381382
</profile>
383+
<profile>
384+
<id>tomcat-weld</id>
385+
<dependencies>
386+
<dependency>
387+
<groupId>org.jboss.arquillian.container</groupId>
388+
<artifactId>arquillian-tomcat-managed-10</artifactId>
389+
<version>1.2.3.Final</version>
390+
<scope>test</scope>
391+
</dependency>
392+
<!-- Weld servlet for testing CDI injections -->
393+
<dependency>
394+
<groupId>org.jboss.weld.servlet</groupId>
395+
<artifactId>weld-servlet-shaded</artifactId>
396+
<version>${weld.version}</version>
397+
<scope>test</scope>
398+
</dependency>
399+
<dependency>
400+
<groupId>jakarta.enterprise.concurrent</groupId>
401+
<artifactId>jakarta.enterprise.concurrent-api</artifactId>
402+
</dependency>
403+
</dependencies>
404+
<build>
405+
<plugins>
406+
<plugin>
407+
<groupId>org.apache.maven.plugins</groupId>
408+
<artifactId>maven-dependency-plugin</artifactId>
409+
<version>${maven-dependency-plugin.version}</version>
410+
<executions>
411+
<execution>
412+
<id>unpack</id>
413+
<phase>process-test-classes</phase>
414+
<goals>
415+
<goal>unpack</goal>
416+
</goals>
417+
<configuration>
418+
<artifactItems>
419+
<artifactItem>
420+
<groupId>org.apache.tomcat</groupId>
421+
<artifactId>tomcat</artifactId>
422+
<version>${apache-tomcat.version}</version>
423+
<type>zip</type>
424+
<outputDirectory>target</outputDirectory>
425+
</artifactItem>
426+
</artifactItems>
427+
</configuration>
428+
</execution>
429+
</executions>
430+
</plugin>
431+
<plugin>
432+
<groupId>org.apache.maven.plugins</groupId>
433+
<artifactId>maven-resources-plugin</artifactId>
434+
<version>3.3.1</version>
435+
<executions>
436+
<execution>
437+
<id>setup-tomcat-users</id>
438+
<goals>
439+
<goal>copy-resources</goal>
440+
</goals>
441+
<phase>process-test-classes</phase>
442+
<configuration>
443+
<resources>
444+
<resource>
445+
<directory>${project.basedir}/src/test/resources/tomcat</directory>
446+
<includes>
447+
<include>tomcat-users.xml</include>
448+
</includes>
449+
</resource>
450+
</resources>
451+
<outputDirectory>${project.build.directory}/apache-tomcat-${apache-tomcat.version}/conf</outputDirectory>
452+
</configuration>
453+
</execution>
454+
</executions>
455+
</plugin>
456+
<plugin>
457+
<artifactId>maven-surefire-plugin</artifactId>
458+
<version>${maven.surefire.plugin.version}</version>
459+
<configuration>
460+
<excludedGroups>com.vaadin.cdi.itest.NeedsCDICompliantContainer,${surefire.excludedGroups}</excludedGroups>
461+
<groups>${surefire.groups}</groups>
462+
<systemPropertyVariables>
463+
<arquillian.launch>tomcat-weld</arquillian.launch>
464+
<catalina.home>${project.build.directory}/apache-tomcat-${apache-tomcat.version}</catalina.home>
465+
</systemPropertyVariables>
466+
</configuration>
467+
</plugin>
468+
</plugins>
469+
</build>
470+
</profile>
382471
<profile>
383472
<id>slowtests</id>
384473
<properties>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2000-2018 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.vaadin.cdi.itest.push;
18+
19+
import java.util.concurrent.Executors;
20+
import java.util.concurrent.ScheduledExecutorService;
21+
22+
import com.vaadin.cdi.annotation.CdiComponent;
23+
24+
@CdiComponent
25+
public class CustomSchedulerPushComponent extends PushComponent {
26+
27+
private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors
28+
.newScheduledThreadPool(4);
29+
30+
@Override
31+
public ScheduledExecutorService getExecutorService() {
32+
return EXECUTOR_SERVICE;
33+
}
34+
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2000-2018 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.vaadin.cdi.itest.push;
18+
19+
import java.util.concurrent.ScheduledExecutorService;
20+
21+
import jakarta.annotation.Resource;
22+
import jakarta.enterprise.concurrent.ManagedScheduledExecutorService;
23+
24+
import com.vaadin.cdi.annotation.CdiComponent;
25+
26+
@CdiComponent
27+
public class ManagedExecutorPushComponent extends PushComponent {
28+
29+
30+
@Resource
31+
private ManagedScheduledExecutorService executorService;
32+
33+
@Override
34+
public ScheduledExecutorService getExecutorService() {
35+
return executorService;
36+
}
37+
38+
}
Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2000-2018 Vaadin Ltd.
2+
* Copyright 2000-2025 Vaadin Ltd.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
55
* use this file except in compliance with the License. You may obtain a copy of
@@ -16,30 +16,27 @@
1616

1717
package com.vaadin.cdi.itest.push;
1818

19+
import java.lang.annotation.Annotation;
20+
import java.util.concurrent.ScheduledExecutorService;
21+
import java.util.concurrent.TimeUnit;
22+
import java.util.concurrent.locks.Lock;
23+
1924
import jakarta.annotation.PostConstruct;
20-
import jakarta.annotation.Resource;
21-
import jakarta.enterprise.concurrent.ManagedScheduledExecutorService;
2225
import jakarta.enterprise.context.ApplicationScoped;
2326
import jakarta.enterprise.context.RequestScoped;
2427
import jakarta.enterprise.context.SessionScoped;
25-
import java.lang.annotation.Annotation;
26-
import java.util.concurrent.TimeUnit;
27-
import java.util.concurrent.locks.Lock;
2828

29-
import com.vaadin.cdi.annotation.CdiComponent;
3029
import com.vaadin.cdi.annotation.RouteScoped;
3130
import com.vaadin.cdi.annotation.UIScoped;
3231
import com.vaadin.cdi.annotation.VaadinServiceScoped;
3332
import com.vaadin.cdi.annotation.VaadinSessionScoped;
3433
import com.vaadin.cdi.util.ContextUtils;
3534
import com.vaadin.flow.component.UI;
3635
import com.vaadin.flow.component.html.Div;
37-
import com.vaadin.flow.component.html.NativeLabel;
3836
import com.vaadin.flow.component.html.NativeButton;
37+
import com.vaadin.flow.component.html.NativeLabel;
3938

40-
@CdiComponent
41-
public class PushComponent extends Div {
42-
39+
public abstract class PushComponent extends Div {
4340
public static final String RUN_BACKGROUND = "RUN_BACKGROUND";
4441
public static final String RUN_FOREGROUND = "RUN_FOREGROUND";
4542

@@ -55,25 +52,25 @@ private ContextCheckTask(UI ui) {
5552

5653
@Override
5754
public void run() {
58-
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
55+
ClassLoader contextClassLoader = Thread.currentThread()
56+
.getContextClassLoader();
5957
Thread.currentThread().setContextClassLoader(this.classLoader);
6058
try {
61-
// We can acquire the lock after the request started this thread is processed
59+
// We can acquire the lock after the request started this thread
60+
// is processed
6261
// Needed to make sure that this is sent as a push message
6362
Lock lockInstance = ui.getSession().getLockInstance();
6463
lockInstance.lock();
6564
lockInstance.unlock();
6665

6766
ui.access(PushComponent.this::print);
6867
} finally {
69-
Thread.currentThread().setContextClassLoader(contextClassLoader);
68+
Thread.currentThread()
69+
.setContextClassLoader(contextClassLoader);
7070
}
7171
}
7272
}
7373

74-
@Resource
75-
private ManagedScheduledExecutorService executorService;
76-
7774
private void print() {
7875
printContextIsActive(RequestScoped.class);
7976
printContextIsActive(SessionScoped.class);
@@ -85,7 +82,8 @@ private void print() {
8582
}
8683

8784
private void printContextIsActive(Class<? extends Annotation> scope) {
88-
NativeLabel label = new NativeLabel(ContextUtils.isContextActive(scope) + "");
85+
NativeLabel label = new NativeLabel(
86+
ContextUtils.isContextActive(scope) + "");
8987
label.setId(scope.getName());
9088
add(new Div(new NativeLabel(scope.getSimpleName() + ": "), label));
9189
}
@@ -94,17 +92,20 @@ private void printContextIsActive(Class<? extends Annotation> scope) {
9492
private void init() {
9593
NativeButton bgButton = new NativeButton("background", event -> {
9694
ContextCheckTask task = new ContextCheckTask(UI.getCurrent());
97-
// Delay execution to prevent UIDL being pushed by the thread serving
95+
// Delay execution to prevent UIDL being pushed by the thread
96+
// serving
9897
// the current HTTP request instead of the background thread
99-
executorService.schedule(task, 10, TimeUnit.MILLISECONDS);
98+
getExecutorService().schedule(task, 10, TimeUnit.MILLISECONDS);
10099
});
101100
bgButton.setId(RUN_BACKGROUND);
102101

103-
NativeButton fgButton = new NativeButton("foreground", event
104-
-> print());
102+
NativeButton fgButton = new NativeButton("foreground",
103+
event -> print());
105104
fgButton.setId(RUN_FOREGROUND);
106105

107106
add(bgButton, fgButton);
108107
}
109108

109+
public abstract ScheduledExecutorService getExecutorService();
110+
110111
}

vaadin-cdi-itest/src/test/java/com/vaadin/cdi/itest/ArchiveProvider.java

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,42 +29,52 @@
2929
public class ArchiveProvider {
3030

3131
public static WebArchive createWebArchive(String warName,
32-
Consumer<WebArchive> customizer) {
32+
Consumer<WebArchive> customizer) {
3333
WebArchive archive = base(warName);
3434
customizer.accept(archive);
3535
return archive;
3636
}
3737

3838
public static WebArchive createWebArchive(String warName,
39-
Class... classes) {
40-
return createWebArchive(warName,
41-
archive -> archive.addClasses(classes)
42-
.addAsResource(new File("target/classes/META-INF")));
39+
Class... classes) {
40+
return createWebArchive(warName, archive -> archive.addClasses(classes)
41+
.addAsResource(new File("target/classes/META-INF")));
4342
}
4443

4544
private static WebArchive base(String warName) {
4645
PomEquippedResolveStage pom = Maven.configureResolver().workOffline()
4746
.loadPomFromFile("target/effective-pom.xml");
48-
WebArchive archive = ShrinkWrap.create(WebArchive.class, warName + ".war")
49-
.addAsLibraries(pom.resolve(
50-
"com.vaadin:vaadin-cdi", "com.vaadin:flow-server",
51-
"com.vaadin:flow-client", "com.vaadin:flow-html-components",
52-
"com.vaadin:flow-polymer-template"
53-
).withTransitivity().asFile())
47+
WebArchive archive = ShrinkWrap
48+
.create(WebArchive.class, warName + ".war")
49+
.addAsLibraries(pom.resolve("com.vaadin:vaadin-cdi",
50+
"com.vaadin:flow-server", "com.vaadin:flow-client",
51+
"com.vaadin:flow-html-components",
52+
"com.vaadin:flow-polymer-template").withTransitivity()
53+
.asFile())
5454
.addAsWebInfResource(EmptyAsset.INSTANCE,
5555
ArchivePaths.create("beans.xml"))
5656
.addClasses(Counter.class, CounterFilter.class);
57-
return applyPayaraSlf4jWorkaround(archive, pom);
57+
return applyContainerConfigurations(archive, pom);
5858
}
5959

60+
private static WebArchive applyContainerConfigurations(WebArchive archive,
61+
PomEquippedResolveStage pom) {
62+
// Testing Tomcat + Weld
63+
if ("tomcat-weld".equals(System.getProperty("arquillian.launch"))) {
64+
archive.addAsLibraries(pom
65+
.resolve("org.slf4j:slf4j-simple",
66+
"org.jboss.weld.servlet:weld-servlet-shaded")
67+
.withoutTransitivity().asFile());
6068

61-
// Workaround for https://github.com/payara/Payara/issues/5898
62-
// Slf4J implementation lookup error in Payara 6
63-
private static WebArchive applyPayaraSlf4jWorkaround(WebArchive archive, PomEquippedResolveStage pom) {
64-
if ("payara".equals(System.getProperty("arquillian.launch"))) {
65-
archive.addAsWebInfResource(AbstractCdiTest.class.getClassLoader().getResource("payara/glassfish-web.xml"),
66-
"glassfish-web.xml")
67-
.addAsLibraries(pom.resolve("org.slf4j:slf4j-simple").withoutTransitivity().asFile());
69+
}
70+
// Workaround for https://github.com/payara/Payara/issues/5898
71+
// Slf4J implementation lookup error in Payara 6
72+
else if ("payara".equals(System.getProperty("arquillian.launch"))) {
73+
archive.addAsWebInfResource(AbstractCdiTest.class.getClassLoader()
74+
.getResource("payara/glassfish-web.xml"),
75+
"glassfish-web.xml")
76+
.addAsLibraries(pom.resolve("org.slf4j:slf4j-simple")
77+
.withoutTransitivity().asFile());
6878

6979
}
7080
return archive;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2000-2018 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.vaadin.cdi.itest;
18+
19+
public interface NeedsCDICompliantContainer {
20+
}

0 commit comments

Comments
 (0)