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.
2222import java .util .Optional ;
2323import java .util .Set ;
2424import java .util .concurrent .ExecutorService ;
25+ import java .util .concurrent .atomic .AtomicBoolean ;
2526import java .util .function .Supplier ;
2627import java .util .logging .Level ;
2728import java .util .logging .Logger ;
2829import java .util .stream .Collectors ;
2930
31+ import javax .annotation .Priority ;
32+ import javax .enterprise .context .ApplicationScoped ;
33+ import javax .enterprise .context .Initialized ;
3034import javax .enterprise .event .Observes ;
3135import javax .enterprise .inject .spi .Extension ;
3236import javax .enterprise .inject .spi .ProcessAnnotatedType ;
4549import org .eclipse .microprofile .config .ConfigProvider ;
4650import 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}
0 commit comments