Skip to content

Commit daad205

Browse files
authored
Merge pull request #29769 from benjamin-confino/28857-openapi-include-exclude
28857 openapi include exclude
2 parents d7b0982 + 86b84fe commit daad205

File tree

35 files changed

+1952
-317
lines changed

35 files changed

+1952
-317
lines changed

dev/fattest.simplicity/src/com/ibm/websphere/simplicity/config/MpOpenAPIElement.java

Lines changed: 167 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,32 @@
99
*******************************************************************************/
1010
package com.ibm.websphere.simplicity.config;
1111

12+
import java.util.ArrayList;
13+
import java.util.Collection;
14+
import java.util.List;
15+
import java.util.logging.Logger;
16+
1217
import javax.xml.bind.annotation.XmlAttribute;
18+
import javax.xml.bind.annotation.XmlElement;
19+
20+
import componenttest.topology.impl.LibertyServer;
1321

1422
public class MpOpenAPIElement extends ConfigElement {
1523

16-
private String docPath;
17-
private String uiPath;
24+
protected String docPath;
25+
protected String uiPath;
26+
27+
@XmlElement(name = "includeApplication")
28+
protected List<String> includedApplications;
29+
30+
@XmlElement(name = "excludeApplication")
31+
protected List<String> excludedApplications;
32+
33+
@XmlElement(name = "includeModule")
34+
protected List<String> includedModules;
35+
36+
@XmlElement(name = "excludeModule")
37+
protected List<String> excludedModules;
1838

1939
/**
2040
* @return the docPath
@@ -46,9 +66,153 @@ public void setUiPath(String uiPath) {
4666
this.uiPath = uiPath;
4767
}
4868

69+
/**
70+
* @return a list of applications to be included.
71+
*/
72+
public List<String> getIncludedApplications() {
73+
return (includedApplications == null) ? (includedApplications = new ArrayList<String>()) : includedApplications;
74+
}
75+
76+
/**
77+
* @return a list of applications to be excluded.
78+
*/
79+
80+
public List<String> getExcludedApplications() {
81+
return (excludedApplications == null) ? (excludedApplications = new ArrayList<String>()) : excludedApplications;
82+
}
83+
84+
/**
85+
* @return a list of modules to be included
86+
*/
87+
public List<String> getIncludedModules() {
88+
return (includedModules == null) ? (includedModules = new ArrayList<String>()) : includedModules;
89+
}
90+
91+
/**
92+
* @return a list of modules to be excluded
93+
*/
94+
public List<String> getExcludedModules() {
95+
return (excludedModules == null) ? (excludedModules = new ArrayList<String>()) : excludedModules;
96+
}
97+
4998
@Override
5099
public String toString() {
51-
return "MpOpenAPIElement [docPath=" + docPath + ", uiPath=" + uiPath + "]";
100+
101+
StringBuilder sb = new StringBuilder();
102+
sb.append(", includeApplication=").append("[" + String.join(",", getIncludedApplications()) + "]");
103+
sb.append(", excludeApplication=").append("[" + String.join(",", getExcludedApplications()) + "]");
104+
sb.append(", includeModule=").append("[" + String.join(",", getIncludedModules()) + "]");
105+
sb.append(", excludeModule=").append("[" + String.join(",", getExcludedModules()) + "]");
106+
107+
return "MpOpenAPIElement [docPath=" + docPath + ", uiPath=" + uiPath + sb.toString() + "]";
108+
}
109+
110+
public static class MpOpenAPIElementBuilder {
111+
112+
private final static Logger LOG = Logger.getLogger("MpOpenAPIElementBuilder");
113+
114+
public static MpOpenAPIElementBuilder cloneBuilderFromServer(LibertyServer server) throws CloneNotSupportedException, Exception {
115+
return new MpOpenAPIElementBuilder(server);
116+
}
117+
118+
public static MpOpenAPIElementBuilder cloneBuilderFromServerResetAppsAndModules(LibertyServer server) throws CloneNotSupportedException, Exception {
119+
MpOpenAPIElementBuilder builder = new MpOpenAPIElementBuilder(server);
120+
nullSafeClear(builder.element.excludedApplications);
121+
nullSafeClear(builder.element.includedApplications);
122+
nullSafeClear(builder.element.excludedModules);
123+
nullSafeClear(builder.element.excludedModules);
124+
return builder;
125+
}
126+
127+
private final MpOpenAPIElement element;
128+
private final LibertyServer server;
129+
private final ServerConfiguration serverConfig;
130+
131+
private MpOpenAPIElementBuilder(LibertyServer server) throws CloneNotSupportedException, Exception {
132+
this.server = server;
133+
this.serverConfig = server.getServerConfiguration().clone();
134+
this.element = serverConfig.getMpOpenAPIElement();
135+
}
136+
137+
public void buildAndPushToServer() throws Exception {
138+
LOG.info("Pushing new server configuration: " + serverConfig.toString());
139+
server.setMarkToEndOfLog();
140+
server.updateServerConfiguration(serverConfig);
141+
if (server.isStarted()) {
142+
server.waitForStringInLogUsingMark("CWWKG0017I"); //The server configuration was successfully updated
143+
//Setting a low timeout because this only appears if OpenAPI trace is enabled (and enabling trace breaks debuggers)
144+
server.waitForStringInLogUsingMark("Finished creating OpenAPI provider", 500);
145+
}
146+
}
147+
148+
public MpOpenAPIElement build() throws Exception {
149+
return element;
150+
}
151+
152+
public void buildAndOverwrite(MpOpenAPIElement other) {
153+
other.docPath = element.docPath;
154+
other.uiPath = element.uiPath;
155+
other.includedApplications = element.getIncludedApplications();
156+
other.excludedApplications = element.getExcludedApplications();
157+
other.includedModules = element.getIncludedModules();
158+
other.excludedModules = element.getExcludedModules();
159+
}
160+
161+
public MpOpenAPIElementBuilder setDocPath(String docPath) {
162+
element.docPath = docPath;
163+
return this;
164+
}
165+
166+
public MpOpenAPIElementBuilder setUiPath(String uiPath) {
167+
element.uiPath = uiPath;
168+
return this;
169+
}
170+
171+
public MpOpenAPIElementBuilder addIncludedApplicaiton(String application) {
172+
element.getIncludedApplications().add(application);
173+
return this;
174+
}
175+
176+
public MpOpenAPIElementBuilder addIncludedApplicaiton(List<String> applications) {
177+
element.getIncludedApplications().addAll(applications);
178+
return this;
179+
}
180+
181+
public MpOpenAPIElementBuilder addIncludedModule(String module) {
182+
element.getIncludedModules().add(module);
183+
return this;
184+
}
185+
186+
public MpOpenAPIElementBuilder addIncludedModule(List<String> module) {
187+
element.getIncludedModules().addAll(module);
188+
return this;
189+
}
190+
191+
public MpOpenAPIElementBuilder addExcludedApplicaiton(String application) {
192+
element.getExcludedApplications().add(application);
193+
return this;
194+
}
195+
196+
public MpOpenAPIElementBuilder addExcludedApplicaiton(List<String> applications) {
197+
element.getExcludedApplications().addAll(applications);
198+
return this;
199+
}
200+
201+
public MpOpenAPIElementBuilder addExcludedModule(String module) {
202+
element.getExcludedModules().add(module);
203+
return this;
204+
}
205+
206+
public MpOpenAPIElementBuilder addExcludedModule(List<String> module) {
207+
element.getExcludedModules().addAll(module);
208+
return this;
209+
}
210+
211+
private static void nullSafeClear(Collection<?> c) {
212+
if (c != null) {
213+
c.clear();
214+
}
215+
}
52216
}
53217

54218
}

dev/fattest.simplicity/src/com/ibm/websphere/simplicity/config/OpenAPIElement.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* are made available under the terms of the Eclipse Public License 2.0
55
* which accompanies this distribution, and is available at
66
* http://www.eclipse.org/legal/epl-2.0/
7-
*
7+
*
88
* SPDX-License-Identifier: EPL-2.0
99
*
1010
* Contributors:

dev/io.openliberty.microprofile.openapi.2.0.internal/bnd.bnd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ IBM-Default-Config: OSGI-INF/wlp/defaultInstances.xml
5555
io.openliberty.microprofile.openapi20.internal.css.CustomCSSProcessor,\
5656
io.openliberty.microprofile.openapi20.internal.DefaultHostListenerImpl,\
5757
io.openliberty.microprofile.openapi20.internal.cache.ConfigSerializer,\
58-
io.openliberty.microprofile.openapi20.internal.validation.OASValidator30Impl
58+
io.openliberty.microprofile.openapi20.internal.validation.OASValidator30Impl,\
59+
io.openliberty.microprofile.openapi20.internal.ModuleSelectionConfigImpl
5960

6061
WS-TraceGroup: MPOPENAPI
6162

dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ApplicationProcessor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import io.openliberty.microprofile.openapi20.internal.cache.ConfigSerializer;
4949
import io.openliberty.microprofile.openapi20.internal.services.ConfigFieldProvider;
5050
import io.openliberty.microprofile.openapi20.internal.services.ModelGenerator;
51+
import io.openliberty.microprofile.openapi20.internal.services.ModuleSelectionConfig;
5152
import io.openliberty.microprofile.openapi20.internal.services.OpenAPIProvider;
5253
import io.openliberty.microprofile.openapi20.internal.utils.Constants;
5354
import io.openliberty.microprofile.openapi20.internal.utils.IndexUtils;

dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ApplicationRegistryImpl.java

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Map.Entry;
2020
import java.util.Objects;
2121

22-
import org.eclipse.microprofile.config.ConfigProvider;
2322
import org.osgi.service.component.annotations.Component;
2423
import org.osgi.service.component.annotations.ConfigurationPolicy;
2524
import org.osgi.service.component.annotations.Reference;
@@ -36,10 +35,11 @@
3635
import com.ibm.ws.kernel.feature.ServerStartedPhase2;
3736
import com.ibm.wsspi.kernel.service.utils.FrameworkState;
3837

38+
import io.openliberty.microprofile.openapi.internal.common.services.OpenAPIAppConfigProvider;
3939
import io.openliberty.microprofile.openapi20.internal.services.ApplicationRegistry;
4040
import io.openliberty.microprofile.openapi20.internal.services.MergeProcessor;
41+
import io.openliberty.microprofile.openapi20.internal.services.ModuleSelectionConfig;
4142
import io.openliberty.microprofile.openapi20.internal.services.OpenAPIProvider;
42-
import io.openliberty.microprofile.openapi20.internal.utils.Constants;
4343
import io.openliberty.microprofile.openapi20.internal.utils.LoggingUtils;
4444
import io.openliberty.microprofile.openapi20.internal.utils.MessageConstants;
4545
import io.openliberty.microprofile.openapi20.internal.utils.ModuleUtils;
@@ -52,8 +52,8 @@
5252
* OpenAPI documentation is generated for each web module and then merged together if merging is enabled. If merging is not enabled,
5353
* then documentation is only generated for the first web module found.
5454
*/
55-
@Component(configurationPolicy = ConfigurationPolicy.IGNORE, service = ApplicationRegistry.class)
56-
public class ApplicationRegistryImpl implements ApplicationRegistry {
55+
@Component(configurationPolicy = ConfigurationPolicy.IGNORE)
56+
public class ApplicationRegistryImpl implements ApplicationRegistry, OpenAPIAppConfigProvider.OpenAPIAppConfigListener {
5757

5858
private static final TraceComponent tc = Tr.register(ApplicationRegistryImpl.class);
5959

@@ -66,13 +66,22 @@ public class ApplicationRegistryImpl implements ApplicationRegistry {
6666
@Reference
6767
private MergeProcessor mergeProcessor;
6868

69+
@Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.STATIC, unbind = "unbindAppConfigListener")
70+
public void bindAppConfigListener(OpenAPIAppConfigProvider openAPIAppConfigProvider) {
71+
openAPIAppConfigProvider.registerAppConfigListener(this);
72+
}
73+
74+
public void unbindAppConfigListener(OpenAPIAppConfigProvider openAPIAppConfigProvider) {
75+
openAPIAppConfigProvider.unregisterAppConfigListener(this);
76+
}
77+
6978
// Thread safety: access to these fields must be synchronized on this
70-
private final Map<String, ApplicationRecord> applications = new LinkedHashMap<>(); // Linked map retains order in which applications were added
79+
private Map<String, ApplicationRecord> applications = new LinkedHashMap<>(); // Linked map retains order in which applications were added
7180

7281
private OpenAPIProvider cachedProvider = null;
7382

74-
// Lazily initialized, use getModuleSelectionConfig() instead
75-
private ModuleSelectionConfig moduleSelectionConfig = null;
83+
@Reference
84+
private ModuleSelectionConfig moduleSelectionConfig;
7685

7786
/**
7887
* The addApplication method is invoked by the {@link ApplicationListener} when it is notified that an application
@@ -95,13 +104,13 @@ public void addApplication(ApplicationInfo newAppInfo) {
95104

96105
OpenAPIProvider firstProvider = getFirstProvider();
97106

98-
if (getModuleSelectionConfig().useFirstModuleOnly() && firstProvider != null) {
107+
if (moduleSelectionConfig.useFirstModuleOnly() && firstProvider != null) {
99108
if (LoggingUtils.isEventEnabled(tc)) {
100109
Tr.event(this, tc, "Application Processor: useFirstModuleOnly is configured and we already have a module. Not processing. appInfo=" + newAppInfo);
101110
}
102111
mergeDisabledAlerter.setUsingMultiModulesWithoutConfig(firstProvider);
103112
} else {
104-
Collection<OpenAPIProvider> openApiProviders = applicationProcessor.processApplication(newAppInfo, getModuleSelectionConfig());
113+
Collection<OpenAPIProvider> openApiProviders = applicationProcessor.processApplication(newAppInfo, moduleSelectionConfig);
105114

106115
if (!openApiProviders.isEmpty()) {
107116
// If the new application has any providers, invalidate the model cache
@@ -139,15 +148,15 @@ public void removeApplication(ApplicationInfo removedAppInfo) {
139148
// If the removed application had any providers, invalidate the provider cache
140149
cachedProvider = null;
141150

142-
if (getModuleSelectionConfig().useFirstModuleOnly() && !FrameworkState.isStopping()) {
151+
if (moduleSelectionConfig.useFirstModuleOnly() && !FrameworkState.isStopping()) {
143152
if (LoggingUtils.isEventEnabled(tc)) {
144153
Tr.event(this, tc, "Application Processor: Current OpenAPI application removed, looking for another application to document.");
145154
}
146155

147156
// We just removed the module used for the OpenAPI document and the server is not shutting down.
148157
// We need to find a new module to use if there is one
149158
for (ApplicationRecord app : applications.values()) {
150-
Collection<OpenAPIProvider> providers = applicationProcessor.processApplication(app.info, getModuleSelectionConfig());
159+
Collection<OpenAPIProvider> providers = applicationProcessor.processApplication(app.info, moduleSelectionConfig);
151160
if (!providers.isEmpty()) {
152161
app.providers.addAll(providers);
153162
break;
@@ -192,9 +201,7 @@ protected void setServerStartPhase2(ServerStartedPhase2 event) {
192201
// Couldn't read this application for some reason, but that means we can't have been able to include modules from it anyway.
193202
}
194203
}
195-
for (String unmatchedInclude : getModuleSelectionConfig().findIncludesNotMatchingAnything(modules)) {
196-
Tr.warning(tc, MessageConstants.OPENAPI_MERGE_UNUSED_INCLUDE_CWWKO1667W, Constants.MERGE_INCLUDE_CONFIG, unmatchedInclude);
197-
}
204+
moduleSelectionConfig.sendWarningsForAppsAndModulesNotMatchingAnything(modules);
198205
}
199206
}
200207

@@ -242,6 +249,9 @@ public OpenAPIProvider getOpenAPIProvider() {
242249
}
243250

244251
cachedProvider = result;
252+
if (LoggingUtils.isEventEnabled(tc)) {
253+
Tr.event(this, tc, "Finished creating OpenAPI provider");
254+
}
245255
return result;
246256
}
247257
}
@@ -254,19 +264,6 @@ private List<OpenAPIProvider> getProvidersToMerge() {
254264
}
255265
}
256266

257-
/**
258-
* Thread safety: Caller must hold lock on {@code this}
259-
*
260-
* @return the module selection config
261-
*/
262-
private ModuleSelectionConfig getModuleSelectionConfig() {
263-
if (moduleSelectionConfig == null) {
264-
// Lazy initialization to avoid calling getConfig() before Config is ready
265-
moduleSelectionConfig = ModuleSelectionConfig.fromConfig(ConfigProvider.getConfig(ApplicationRegistryImpl.class.getClassLoader()));
266-
}
267-
return moduleSelectionConfig;
268-
}
269-
270267
/**
271268
* Thread safety: Caller must hold lock on {@code this}
272269
*
@@ -282,6 +279,20 @@ private OpenAPIProvider getFirstProvider() {
282279
return null;
283280
}
284281

282+
@Override
283+
public void processConfigUpdate() {
284+
synchronized (this) {
285+
Map<String, ApplicationRecord> oldApps = applications;
286+
applications = new LinkedHashMap<>();
287+
for (ApplicationRecord record : oldApps.values()) {
288+
//Add application uses config to decide if it creates and registers any providers in ApplicationInfo
289+
//Rather than map from the old state to the new state when the config changes, KISS and start again.
290+
addApplication(record.info);
291+
}
292+
cachedProvider = null;
293+
}
294+
}
295+
285296
private static class ApplicationRecord {
286297
public ApplicationRecord(ApplicationInfo info) {
287298
this.info = info;
@@ -291,4 +302,10 @@ public ApplicationRecord(ApplicationInfo info) {
291302
private final List<OpenAPIProvider> providers = new ArrayList<>();
292303
}
293304

305+
//This is to ensure we're called after ModuleSelectionConfigImpl
306+
@Override
307+
public int getConfigListenerPriority() {
308+
return 2;
309+
}
310+
294311
}

0 commit comments

Comments
 (0)