Skip to content

Commit 2ca4b74

Browse files
authored
Safeguard against JAX-RS app modifications after start. (helidon-io#1486)
Signed-off-by: Tomas Langer <tomas.langer@oracle.com>
1 parent 57e5771 commit 2ca4b74

2 files changed

Lines changed: 71 additions & 21 deletions

File tree

microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsCdiExtension.java

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2020 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,11 +22,15 @@
2222
import java.util.Optional;
2323
import java.util.Set;
2424
import java.util.concurrent.ExecutorService;
25+
import java.util.concurrent.atomic.AtomicBoolean;
2526
import java.util.function.Supplier;
2627
import java.util.logging.Level;
2728
import java.util.logging.Logger;
2829
import java.util.stream.Collectors;
2930

31+
import javax.annotation.Priority;
32+
import javax.enterprise.context.ApplicationScoped;
33+
import javax.enterprise.context.Initialized;
3034
import javax.enterprise.event.Observes;
3135
import javax.enterprise.inject.spi.Extension;
3236
import javax.enterprise.inject.spi.ProcessAnnotatedType;
@@ -45,6 +49,8 @@
4549
import org.eclipse.microprofile.config.ConfigProvider;
4650
import org.glassfish.jersey.server.ResourceConfig;
4751

52+
import static javax.interceptor.Interceptor.Priority.PLATFORM_BEFORE;
53+
4854
/**
4955
* Configure Jersey related things.
5056
*/
@@ -59,6 +65,7 @@ public class JaxRsCdiExtension implements Extension {
5965

6066
private final Set<Class<? extends Application>> applications = new LinkedHashSet<>();
6167
private final Set<Class<?>> resources = new HashSet<>();
68+
private final AtomicBoolean setInStone = new AtomicBoolean(false);
6269

6370
private void collectApplications(@Observes ProcessAnnotatedType<? extends Application> applicationType) {
6471
applications.add(applicationType.getAnnotatedType().getJavaClass());
@@ -74,12 +81,26 @@ private void collectResourceClasses(@Observes @WithAnnotations(Path.class) Proce
7481
resources.add(resourceClass);
7582
}
7683

84+
// once application scoped starts, we do not allow modification of applications
85+
void fixApps(@Observes @Priority(PLATFORM_BEFORE) @Initialized(ApplicationScoped.class) Object event) {
86+
this.setInStone.set(true);
87+
}
88+
7789
/**
7890
* List of applications including discovered and explicitly configured applications.
91+
* <p>
92+
* This method should only be called in {@code Initialized(ApplicationScoped.class)} observer methods,
93+
* that have a higher priority than {@link io.helidon.microprofile.server.ServerCdiExtension} start server
94+
* method.
7995
*
8096
* @return list of applications found by CDI
97+
* @throws java.lang.IllegalStateException in case the list of applications is not yet fixed
8198
*/
82-
public List<JaxRsApplication> applicationsToRun() {
99+
public List<JaxRsApplication> applicationsToRun() throws IllegalStateException {
100+
if (!setInStone.get()) {
101+
throw new IllegalStateException("Applications are not yet fixed. This method is only available in "
102+
+ "@Initialized(ApplicationScoped.class) event, before server is started");
103+
}
83104
if (applications.isEmpty() && applicationMetas.isEmpty()) {
84105
// create a synthetic application from all resource classes
85106
// the classes set must be created before the lambda, as resources are cleared later on
@@ -117,15 +138,21 @@ public Set<Class<?>> getClasses() {
117138

118139
/**
119140
* Remove all discovered applications (configured applications are not removed).
141+
*
142+
* @throws java.lang.IllegalStateException in case applications are already started
120143
*/
121-
public void removeApplications() {
144+
public void removeApplications() throws IllegalStateException {
145+
mutateApps();
122146
this.applications.clear();
123147
}
124148

125149
/**
126150
* Remove all discovered and configured resource classes.
151+
*
152+
* @throws java.lang.IllegalStateException in case applications are already started
127153
*/
128-
public void removeResourceClasses() {
154+
public void removeResourceClasses() throws IllegalStateException {
155+
mutateApps();
129156
this.resources.clear();
130157
}
131158

@@ -135,18 +162,23 @@ public void removeResourceClasses() {
135162
* on other configuration.
136163
*
137164
* @param resourceClasses resource classes to add
165+
* @throws java.lang.IllegalStateException in case applications are already started
138166
*/
139-
public void addResourceClasses(List<Class<?>> resourceClasses) {
167+
public void addResourceClasses(List<Class<?>> resourceClasses) throws IllegalStateException {
168+
mutateApps();
140169
this.resources.addAll(resourceClasses);
141170
}
142171

143172
/**
144173
* Add all application metadata from the provided list.
145174
*
146175
* @param applications application metadata
176+
* @throws java.lang.IllegalStateException in case applications are already started
177+
*
147178
* @see io.helidon.microprofile.server.JaxRsApplication
148179
*/
149-
public void addApplications(List<JaxRsApplication> applications) {
180+
public void addApplications(List<JaxRsApplication> applications) throws IllegalStateException {
181+
mutateApps();
150182
this.applicationMetas.addAll(applications);
151183
}
152184

@@ -155,8 +187,10 @@ public void addApplications(List<JaxRsApplication> applications) {
155187
* You can also use {@link #addApplication(String, Application)}.
156188
*
157189
* @param application configured as needed
190+
* @throws java.lang.IllegalStateException in case applications are already started
158191
*/
159-
public void addApplication(Application application) {
192+
public void addApplication(Application application) throws IllegalStateException {
193+
mutateApps();
160194
this.applicationMetas.add(JaxRsApplication.create(application));
161195
}
162196

@@ -165,25 +199,16 @@ public void addApplication(Application application) {
165199
*
166200
* @param contextRoot Context root to use for this application ({@link javax.ws.rs.ApplicationPath} is ignored)
167201
* @param application configured as needed
202+
* @throws java.lang.IllegalStateException in case applications are already started
168203
*/
169-
public void addApplication(String contextRoot, Application application) {
204+
public void addApplication(String contextRoot, Application application) throws IllegalStateException {
205+
mutateApps();
170206
this.applicationMetas.add(JaxRsApplication.builder()
171207
.application(application)
172208
.contextRoot(contextRoot)
173209
.build());
174210
}
175211

176-
/**
177-
* Access existing applications explicitly configured. Does not include discovered applications.
178-
*
179-
* @return list of all applications
180-
*/
181-
public List<ResourceConfig> applications() {
182-
return applicationMetas.stream()
183-
.map(JaxRsApplication::resourceConfig)
184-
.collect(Collectors.toList());
185-
}
186-
187212
/**
188213
* Makes an attempt to "guess" the service name.
189214
* <p>
@@ -215,8 +240,10 @@ private Optional<String> guessServiceName() {
215240
* Create an application from the provided resource classes and add it to the list of applications.
216241
*
217242
* @param resourceClasses resource classes to create a synthetic application from
243+
* @throws java.lang.IllegalStateException in case applications are already started
218244
*/
219-
public void addSyntheticApplication(List<Class<?>> resourceClasses) {
245+
public void addSyntheticApplication(List<Class<?>> resourceClasses) throws IllegalStateException {
246+
mutateApps();
220247
this.applicationMetas.add(JaxRsApplication.builder()
221248
.applicationClass(Application.class)
222249
.config(ResourceConfig.forApplication(new Application() {
@@ -265,4 +292,11 @@ boolean isNamedRoutingRequired(io.helidon.config.Config config, JaxRsApplication
265292
.asBoolean()
266293
.orElseGet(jaxRsApplication::routingNameRequired);
267294
}
295+
296+
private void mutateApps() {
297+
if (setInStone.get()) {
298+
throw new IllegalStateException("You are attempting to modify applications in JAX-RS after they were registered "
299+
+ "with the server");
300+
}
301+
}
268302
}

microprofile/server/src/test/java/io/helidon/microprofile/server/JaxRsCdiExtensionTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2020 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
1515
*/
1616
package io.helidon.microprofile.server;
1717

18+
import java.util.List;
1819
import java.util.Map;
1920
import java.util.Optional;
2021

@@ -28,6 +29,7 @@
2829
import static io.helidon.config.testing.OptionalMatcher.value;
2930
import static org.hamcrest.CoreMatchers.is;
3031
import static org.hamcrest.MatcherAssert.assertThat;
32+
import static org.junit.jupiter.api.Assertions.assertThrows;
3133

3234
/**
3335
* Unit test for {@link io.helidon.microprofile.server.JaxRsCdiExtension}.
@@ -157,4 +159,18 @@ void testContextRootNoConfigWithTrailingSlash() {
157159
Optional<String> contextRoot = extension.findContextRoot(EMPTY_CONFIG, app);
158160
assertThat(contextRoot, value(is("/myApp")));
159161
}
162+
163+
@Test
164+
void testAppsNotModifiableAfterUse() {
165+
JaxRsCdiExtension ext = new JaxRsCdiExtension();
166+
ext.fixApps(new Object());
167+
168+
assertThrows(IllegalStateException.class, () -> ext.addApplication(new JaxRsApplicationTest.MyApplication()));
169+
assertThrows(IllegalStateException.class, () -> ext.addApplication("/", new JaxRsApplicationTest.MyApplication()));
170+
assertThrows(IllegalStateException.class, () -> ext.addApplications(List.of()));
171+
assertThrows(IllegalStateException.class, () -> ext.addResourceClasses(List.of()));
172+
assertThrows(IllegalStateException.class, () -> ext.addSyntheticApplication(List.of()));
173+
assertThrows(IllegalStateException.class, ext::removeApplications);
174+
assertThrows(IllegalStateException.class, ext::removeResourceClasses);
175+
}
160176
}

0 commit comments

Comments
 (0)