This is an automated email from the ASF dual-hosted git repository.

jlmonteiro pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit a79397ac32b10ba969095b40b73c6be1e783ae37
Author: Jean-Louis Monteiro <[email protected]>
AuthorDate: Fri Sep 2 11:37:53 2022 +0200

    TOMEE-3900 Integrate SmallRye OpenAPI and pass TCK
---
 .../java/org/apache/openejb/util/AppFinder.java    |   4 +
 tck/microprofile-tck/openapi/pom.xml               |  41 +++--
 .../MicroProfileOpenAPITCKDeploymentPackager.java  |   7 +-
 .../MicroProfileOpenAPITCKExtension.java           |   2 +
 .../MicroProfileOpenApiDeploymentProcessor.java    | 185 +++++++++++++++++++++
 .../openapi/src/test/resources/arquillian.xml      |   1 +
 tck/microprofile-tck/openapi/tck-dev.xml           |  25 +++
 tck/microprofile-tck/opentracing/pom.xml           |   2 +-
 tck/microprofile-tck/pom.xml                       |   2 +-
 .../microprofile/TomEEMicroProfileListener.java    |   3 +-
 ...r.java => MicroProfileMetricsRegistration.java} |   9 +-
 .../openapi/MicroProfileOpenApiEndpoint.java       |  59 +++++++
 .../openapi/MicroProfileOpenApiRegistration.java   | 116 +++++++++++++
 .../jakarta.servlet.ServletContainerInitializer    |   3 +-
 14 files changed, 431 insertions(+), 28 deletions(-)

diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/util/AppFinder.java 
b/container/openejb-core/src/main/java/org/apache/openejb/util/AppFinder.java
index cd8cf81103..dd53848e42 100644
--- 
a/container/openejb-core/src/main/java/org/apache/openejb/util/AppFinder.java
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/util/AppFinder.java
@@ -25,6 +25,10 @@ import org.apache.webbeans.config.WebBeansContext;
 public final class AppFinder {
     public static <T> T findAppContextOrWeb(final ClassLoader cl, final 
Transformer<T> transformer) {
         final ContainerSystem containerSystem = 
SystemInstance.get().getComponent(ContainerSystem.class);
+        if (containerSystem == null) {
+            return null; // when used before the container even gets a chance 
to start
+        }
+
         for (final AppContext appContext : containerSystem.getAppContexts()) {
             final ClassLoader appContextClassLoader = 
appContext.getClassLoader();
             boolean found = false;
diff --git a/tck/microprofile-tck/openapi/pom.xml 
b/tck/microprofile-tck/openapi/pom.xml
index bd240b0ce1..c0fc18ce45 100644
--- a/tck/microprofile-tck/openapi/pom.xml
+++ b/tck/microprofile-tck/openapi/pom.xml
@@ -38,15 +38,6 @@
           <dependenciesToScan>
             
<dependency>org.eclipse.microprofile.openapi:microprofile-openapi-tck</dependency>
           </dependenciesToScan>
-          <excludes>
-            <!-- TODO - Expecting UserResource to be deployed, but not 
included in the JAXRSApp. Open issue in project. -->
-            
<exclude>org.eclipse.microprofile.openapi.tck.OASConfigServersTest</exclude>
-            <!-- TODO - Possible bug in the implementation - Test fail due to 
Health endpoint being added to the OpenAPI result -->
-            <!-- Only endpoints set up in Application should be added. Check 
org.apache.geronimo.microprofile.openapi.cdi.GeronimoOpenAPIExtension.createOpenApi
 -->
-            
<exclude>org.eclipse.microprofile.openapi.tck.OASConfigExcludePackageTest</exclude>
-            
<exclude>org.eclipse.microprofile.openapi.tck.OASConfigExcludeClassTest</exclude>
-            
<exclude>org.eclipse.microprofile.openapi.tck.OASConfigExcludeClassesTest</exclude>
-          </excludes>
           <systemPropertyVariables>
             <test.url>http://localhost:50290</test.url>
           </systemPropertyVariables>
@@ -108,8 +99,8 @@
       <scope>test</scope>
       <exclusions>
         <exclusion>
-          <groupId>javax.ws.rs</groupId>
-          <artifactId>javax.ws.rs-api</artifactId>
+          <groupId>jakarta.ws.rs</groupId>
+          <artifactId>jakarta.ws.rs-api</artifactId>
         </exclusion>
       </exclusions>
     </dependency>
@@ -126,7 +117,33 @@
     <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
-      <version>6.14.3</version>
+      <!-- 7.4.0 is broken, so until 7.5.0 is released ... -->
+      <version>7.3.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-all</artifactId>
+      <version>1.3</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tomee</groupId>
+      <artifactId>ziplock</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.arquillian.testng</groupId>
+      <artifactId>arquillian-testng-container</artifactId>
+      <version>1.7.0.Alpha10</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.tomee</groupId>
+      <artifactId>mp-common</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
 
diff --git 
a/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKDeploymentPackager.java
 
b/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKDeploymentPackager.java
index 39e390fa89..67c49a954b 100644
--- 
a/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKDeploymentPackager.java
+++ 
b/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKDeploymentPackager.java
@@ -16,6 +16,8 @@
  */
 package org.apache.tomee.microprofile.tck.openapi;
 
+import org.apache.ziplock.JarLocation;
+import org.hamcrest.Matchers;
 import org.jboss.arquillian.container.test.spi.TestDeployment;
 import 
org.jboss.arquillian.container.test.spi.client.deployment.ProtocolArchiveProcessor;
 import 
org.jboss.arquillian.protocol.servlet5.v_5.ServletProtocolDeploymentPackager;
@@ -35,10 +37,7 @@ public class MicroProfileOpenAPITCKDeploymentPackager 
extends ServletProtocolDep
                                          final 
Collection<ProtocolArchiveProcessor> processors) {
         final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, 
"microprofile-openapi.war")
                                                 
.merge(testDeployment.getApplicationArchive())
-                                                // TODO - This doesn't seem 
right. This is for the JAX-RS endpoints to be CDI scanned.
-                                                // This is to use CDI events 
to filter endpoints with configuration.
-                                                // Check 
org.apache.geronimo.microprofile.openapi.cdi.GeronimoOpenAPIExtension.findEndpointsAndApplication()
-                                                // A beans.xml should not be 
required.
+                                                
.addAsLibrary(JarLocation.jarLocation(Matchers.class)) // required for 
ModelConstructionTest
                                                 
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                 ;
 
diff --git 
a/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKExtension.java
 
b/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKExtension.java
index f81544c07d..a5ecb35902 100644
--- 
a/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKExtension.java
+++ 
b/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenAPITCKExtension.java
@@ -16,6 +16,7 @@
  */
 package org.apache.tomee.microprofile.tck.openapi;
 
+import 
org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
 import org.jboss.arquillian.container.test.spi.client.protocol.Protocol;
 import org.jboss.arquillian.core.spi.LoadableExtension;
 import org.jboss.arquillian.protocol.servlet5.v_5.ServletProtocol;
@@ -25,6 +26,7 @@ public class MicroProfileOpenAPITCKExtension implements 
LoadableExtension {
     public void register(final ExtensionBuilder extensionBuilder) {
         extensionBuilder
                 .override(Protocol.class, ServletProtocol.class, 
MicroProfileOpenAPITCKProtocol.class)
+                .service(ApplicationArchiveProcessor.class, 
MicroProfileOpenApiDeploymentProcessor.class)
                 .observer(MicroProfileOpenAPITCKObserver.class)
         ;
     }
diff --git 
a/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenApiDeploymentProcessor.java
 
b/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenApiDeploymentProcessor.java
new file mode 100644
index 0000000000..2b9c9b6eb6
--- /dev/null
+++ 
b/tck/microprofile-tck/openapi/src/test/java/org.apache.tomee.microprofile.tck.openapi/MicroProfileOpenApiDeploymentProcessor.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.microprofile.tck.openapi;
+
+import io.smallrye.config.PropertiesConfigSource;
+import io.smallrye.config.SmallRyeConfig;
+import io.smallrye.config.SmallRyeConfigBuilder;
+import io.smallrye.openapi.api.OpenApiConfig;
+import io.smallrye.openapi.api.OpenApiConfigImpl;
+import io.smallrye.openapi.api.OpenApiDocument;
+import io.smallrye.openapi.runtime.OpenApiProcessor;
+import io.smallrye.openapi.runtime.OpenApiStaticFile;
+import io.smallrye.openapi.runtime.io.Format;
+import io.smallrye.openapi.runtime.scanner.FilteredIndexView;
+import org.apache.tomee.microprofile.openapi.MicroProfileOpenApiEndpoint;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.eclipse.microprofile.openapi.models.OpenAPI;
+import 
org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
+import org.jboss.arquillian.test.spi.TestClass;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.Index;
+import org.jboss.jandex.Indexer;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.Node;
+import org.jboss.shrinkwrap.api.asset.ByteArrayAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+
+import static io.smallrye.openapi.runtime.OpenApiProcessor.getFilter;
+import static 
io.smallrye.openapi.runtime.OpenApiProcessor.modelFromAnnotations;
+import static io.smallrye.openapi.runtime.OpenApiProcessor.modelFromReader;
+import static io.smallrye.openapi.runtime.io.Format.JSON;
+import static io.smallrye.openapi.runtime.io.Format.YAML;
+import static io.smallrye.openapi.runtime.io.OpenApiSerializer.serialize;
+import static java.lang.Thread.currentThread;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Optional.ofNullable;
+
+/**
+ * Could probably be merged into the DeploymentProcessor. It's responsible for 
generating an OpenAPI document
+ * and add it into the archive.
+ */
+public class MicroProfileOpenApiDeploymentProcessor implements 
ApplicationArchiveProcessor {
+    private static Logger LOGGER = 
Logger.getLogger(MicroProfileOpenApiDeploymentProcessor.class.getName());
+    public static volatile ClassLoader classLoader;
+
+    @Override
+    public void process(Archive<?> archive, TestClass testClass) {
+        if (classLoader == null) {
+            classLoader = Thread.currentThread().getContextClassLoader();
+        }
+
+        if (archive instanceof WebArchive) {
+            WebArchive war = (WebArchive) archive;
+
+            generateOpenAPI(war);
+
+            LOGGER.log(Level.FINE, () -> war.toString(true));
+        }
+    }
+
+    /**
+     * Builds the OpenAPI file and copies it to the deployed application.
+     */
+    private static void generateOpenAPI(final WebArchive war) {
+        OpenApiConfig openApiConfig = config(war);
+        Index index = index(war, openApiConfig);
+        ClassLoader contextClassLoader = 
currentThread().getContextClassLoader();
+
+        Optional<OpenAPI> annotationModel = 
ofNullable(modelFromAnnotations(openApiConfig, contextClassLoader, index));
+        Optional<OpenAPI> readerModel = 
ofNullable(modelFromReader(openApiConfig, contextClassLoader));
+        Optional<OpenAPI> staticFileModel = Stream.of(modelFromFile(war, 
"/META-INF/openapi.json", JSON),
+                modelFromFile(war, "/META-INF/openapi.yaml", YAML),
+                modelFromFile(war, "/META-INF/openapi.yml", YAML))
+                .filter(Optional::isPresent)
+                .findFirst()
+                .flatMap(openAPI -> openAPI);
+
+        OpenApiDocument document = OpenApiDocument.INSTANCE;
+        document.reset();
+        document.config(openApiConfig);
+        annotationModel.ifPresent(document::modelFromAnnotations);
+        readerModel.ifPresent(document::modelFromReader);
+        staticFileModel.ifPresent(document::modelFromStaticFile);
+        document.filter(getFilter(openApiConfig, contextClassLoader));
+        document.initialize();
+        OpenAPI openAPI = document.get();
+
+        try {
+            war.addAsManifestResource(new ByteArrayAsset(serialize(openAPI, 
JSON).getBytes(UTF_8)), "openapi.json");
+            war.addAsManifestResource(new ByteArrayAsset(serialize(openAPI, 
YAML).getBytes(UTF_8)), "openapi.yaml");
+        } catch (IOException e) {
+            // Ignore
+        }
+
+        document.reset();
+    }
+
+    /**
+     * Provides the Jandex index.
+     */
+    private static Index index(final WebArchive war, final OpenApiConfig 
config) {
+        FilteredIndexView filteredIndexView = new FilteredIndexView(null, 
config);
+        Indexer indexer = new Indexer();
+        Collection<Node> classes = war.getContent(object -> 
object.get().endsWith(".class")).values();
+        for (Node value : classes) {
+            try {
+                String resource = 
value.getPath().get().replaceAll("/WEB-INF/classes/", "");
+                // We remove the OpenApinEndpoint so the /openapi is not 
generated
+                if 
(resource.contains(MicroProfileOpenApiEndpoint.class.getSimpleName())) {
+                    continue;
+                }
+
+                DotName dotName = 
DotName.createSimple(resource.replaceAll("/", ".").substring(0, 
resource.length() - 6));
+                if (filteredIndexView.accepts(dotName)) {
+                    
indexer.index(MicroProfileOpenApiDeploymentProcessor.class.getClassLoader().getResourceAsStream(resource));
+                }
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+        return indexer.complete();
+    }
+
+    /**
+     * Creates the config from the microprofile-config.properties file in the 
application. The spec defines that the
+     * config file may be present in two locations.
+     */
+    private static OpenApiConfig config(final WebArchive war) {
+        Optional<Node> microprofileConfig = 
Stream.of(ofNullable(war.get("/META-INF/microprofile-config.properties")),
+                
ofNullable(war.get("/WEB-INF/classes/META-INF/microprofile-config.properties")))
+                .filter(Optional::isPresent)
+                .findFirst()
+                .flatMap(node -> node);
+
+        if (!microprofileConfig.isPresent()) {
+            return new OpenApiConfigImpl(ConfigProvider.getConfig());
+        }
+
+        Properties properties = new Properties();
+        try (InputStreamReader reader = new 
InputStreamReader(microprofileConfig.get().getAsset().openStream(), UTF_8)) {
+            properties.load(reader);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        SmallRyeConfig config = new SmallRyeConfigBuilder()
+                .addDefaultSources()
+                .addDefaultInterceptors()
+                .withSources(new PropertiesConfigSource(properties, 
"microprofile-config.properties"))
+                .build();
+
+        return new OpenApiConfigImpl(config);
+    }
+
+    private static Optional<OpenAPI> modelFromFile(final WebArchive war, final 
String location,
+            final Format format) {
+        return ofNullable(war.get(location))
+                .map(Node::getAsset)
+                .map(asset -> new OpenApiStaticFile(asset.openStream(), 
format))
+                .map(OpenApiProcessor::modelFromStaticFile);
+    }
+}
diff --git a/tck/microprofile-tck/openapi/src/test/resources/arquillian.xml 
b/tck/microprofile-tck/openapi/src/test/resources/arquillian.xml
index 669ed090fe..5fea36e73b 100644
--- a/tck/microprofile-tck/openapi/src/test/resources/arquillian.xml
+++ b/tck/microprofile-tck/openapi/src/test/resources/arquillian.xml
@@ -27,6 +27,7 @@
       <property name="ajpPort">-1</property>
       <property name="stopPort">-1</property>
       <property name="classifier">microprofile</property>
+      <property name="debug">false</property>
       <property name="conf">src/test/conf</property>
       <property name="dir">target/tomee</property>
       <property name="appWorkingDir">target/workdir</property>
diff --git a/tck/microprofile-tck/openapi/tck-dev.xml 
b/tck/microprofile-tck/openapi/tck-dev.xml
new file mode 100644
index 0000000000..848c59732c
--- /dev/null
+++ b/tck/microprofile-tck/openapi/tck-dev.xml
@@ -0,0 +1,25 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<suite name="microprofile-openapi-TCK" verbose="2" 
configfailurepolicy="continue" >
+
+  <test name="microprofile-openapi TCK">
+    <classes>
+      <class 
name="org.eclipse.microprofile.openapi.tck.ModelConstructionTest"></class>
+    </classes>
+  </test>
+
+</suite>
\ No newline at end of file
diff --git a/tck/microprofile-tck/opentracing/pom.xml 
b/tck/microprofile-tck/opentracing/pom.xml
index a96cc70657..ff542b3525 100644
--- a/tck/microprofile-tck/opentracing/pom.xml
+++ b/tck/microprofile-tck/opentracing/pom.xml
@@ -118,7 +118,7 @@
     <dependency>
       <groupId>org.apache.tomee</groupId>
       <artifactId>cxf-rt-rs-mp-client-shade</artifactId>
-      <version>9.0.0-M9-SNAPSHOT</version>
+      <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
 
diff --git a/tck/microprofile-tck/pom.xml b/tck/microprofile-tck/pom.xml
index acaf6f6869..da59e095b7 100644
--- a/tck/microprofile-tck/pom.xml
+++ b/tck/microprofile-tck/pom.xml
@@ -35,10 +35,10 @@
     <module>health</module>
     <module>metrics</module>
     <module>rest-client</module>
+    <module>openapi</module>
     <!--
     <module>opentracing</module>
     <module>fault-tolerance</module>
-    <module>openapi</module>
     -->
   </modules>
 </project>
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/TomEEMicroProfileListener.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/TomEEMicroProfileListener.java
index 33d81a9b6e..4b3502a9b0 100644
--- 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/TomEEMicroProfileListener.java
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/TomEEMicroProfileListener.java
@@ -28,6 +28,7 @@ import org.apache.openejb.util.Logger;
 import org.apache.tomee.catalina.event.AfterApplicationCreated;
 import org.apache.tomee.installer.Paths;
 import org.apache.tomee.microprofile.health.MicroProfileHealthChecksEndpoint;
+import org.apache.tomee.microprofile.openapi.MicroProfileOpenApiEndpoint;
 
 import java.io.File;
 import java.net.MalformedURLException;
@@ -105,7 +106,7 @@ public class TomEEMicroProfileListener {
 
         // These remove duplicated REST API endpoints.
         // webApp.restClass.removeIf(className -> 
className.equals(MicroProfileHealthChecksEndpoint.class.getName()));
-        // webApp.restClass.removeIf(className -> 
className.equals(MetricsEndpoints.class.getName()));
+        // webApp.restClass.removeIf(className -> 
className.equals(MicroProfileOpenApiEndpoint.class.getName()));
 
         // There remove all of MP REST API endpoint if there is a servlet 
already registered in /*. The issue here is
         // that REST path has priority over servlet and there may override old 
applications that have servlets
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/metrics/MicroProfileServletContainerInitializer.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/metrics/MicroProfileMetricsRegistration.java
similarity index 83%
rename from 
tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/metrics/MicroProfileServletContainerInitializer.java
rename to 
tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/metrics/MicroProfileMetricsRegistration.java
index 93b3956e89..6ce87a4611 100644
--- 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/metrics/MicroProfileServletContainerInitializer.java
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/metrics/MicroProfileMetricsRegistration.java
@@ -22,13 +22,6 @@ import jakarta.servlet.ServletContainerInitializer;
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.ServletRegistration;
-import jakarta.servlet.annotation.HandlesTypes;
-import jakarta.servlet.annotation.WebFilter;
-import jakarta.servlet.annotation.WebServlet;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Application;
-import org.apache.tomee.microprofile.jwt.MPJWTFilter;
-import org.eclipse.microprofile.auth.LoginConfig;
 
 import java.util.Set;
 
@@ -41,7 +34,7 @@ import java.util.Set;
                WebFilter.class
 })
  */
-public class MicroProfileServletContainerInitializer implements 
ServletContainerInitializer {
+public class MicroProfileMetricsRegistration implements 
ServletContainerInitializer {
 
     @Override
     public void onStartup(final Set<Class<?>> classes, final ServletContext 
ctx) throws ServletException {
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/openapi/MicroProfileOpenApiEndpoint.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/openapi/MicroProfileOpenApiEndpoint.java
new file mode 100644
index 0000000000..09ae231b5f
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/openapi/MicroProfileOpenApiEndpoint.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.microprofile.openapi;
+
+import io.smallrye.openapi.runtime.io.Format;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.microprofile.openapi.models.OpenAPI;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import static io.smallrye.openapi.runtime.io.Format.JSON;
+import static io.smallrye.openapi.runtime.io.Format.YAML;
+import static io.smallrye.openapi.runtime.io.OpenApiSerializer.serialize;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * This is not a JAXRS endpoint but a regular servlet because the Smallrye 
does the remaining job. We could do it as
+ * a regular REST endpoint though.
+ */
+@WebServlet(name = "openapi-servlet", urlPatterns = "/openapi/*")
+public class MicroProfileOpenApiEndpoint extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+        final ServletContext servletContext = request.getServletContext();
+        final OpenAPI openAPI = 
MicroProfileOpenApiRegistration.getOpenApi(servletContext);
+        final String format = request.getParameter("format");
+        final Format formatOpenApi = getOpenApiFormat(request, format);
+        response.setContentType(formatOpenApi.getMimeType());
+        response.getOutputStream().write(serialize(openAPI, 
formatOpenApi).getBytes(UTF_8));
+    }
+
+    private Format getOpenApiFormat(final HttpServletRequest request, final 
String format) {
+        return Stream.of(Format.values())
+                     .filter(f -> format != null && 
f.name().compareToIgnoreCase(format) == 0)
+                     .findFirst()
+                     
.orElse(request.getHeader("Accept").toLowerCase(Locale.ENGLISH).contains("application/json")
 ? JSON : YAML);
+    }
+}
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/openapi/MicroProfileOpenApiRegistration.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/openapi/MicroProfileOpenApiRegistration.java
new file mode 100644
index 0000000000..a1e1785e6d
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/openapi/MicroProfileOpenApiRegistration.java
@@ -0,0 +1,116 @@
+/*
+ *     Licensed to the Apache Software Foundation (ASF) under one or more
+ *     contributor license agreements.  See the NOTICE file distributed with
+ *     this work for additional information regarding copyright ownership.
+ *     The ASF licenses this file to You under the Apache License, Version 2.0
+ *     (the "License"); you may not use this file except in compliance with
+ *     the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *     Unless required by applicable law or agreed to in writing, software
+ *     distributed under the License is distributed on an "AS IS" BASIS,
+ *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *     See the License for the specific language governing permissions and
+ *     limitations under the License.
+ */
+package org.apache.tomee.microprofile.openapi;
+
+import io.smallrye.openapi.api.OpenApiConfig;
+import io.smallrye.openapi.api.OpenApiConfigImpl;
+import io.smallrye.openapi.api.OpenApiDocument;
+import io.smallrye.openapi.runtime.OpenApiProcessor;
+import io.smallrye.openapi.runtime.OpenApiStaticFile;
+import io.smallrye.openapi.runtime.io.Format;
+import jakarta.servlet.ServletContainerInitializer;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRegistration;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.eclipse.microprofile.openapi.models.OpenAPI;
+
+import java.net.URL;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static io.smallrye.openapi.runtime.io.Format.JSON;
+import static io.smallrye.openapi.runtime.io.Format.YAML;
+
+/**
+ * Responsible for adding the filter into the chain and doing all other 
initialization
+ * <p>
+ * todo do we want to be so restrictive with the HandlesTypes annotation?
+ *
+ * @HandlesTypes({Path.class, WebServlet.class,
+ * WebFilter.class
+ * })
+ */
+public class MicroProfileOpenApiRegistration implements 
ServletContainerInitializer {
+
+    @Override
+    public void onStartup(final Set<Class<?>> classes, final ServletContext 
ctx) throws ServletException {
+        final ServletRegistration.Dynamic servletRegistration =
+            ctx.addServlet("mp-openapi-servlet", 
MicroProfileOpenApiEndpoint.class);
+        servletRegistration.addMapping("/openapi/*");
+
+        openApi(ctx);
+    }
+
+    private void openApi(final ServletContext servletContext) {
+        try {
+            Optional<OpenAPI> staticOpenApi = Stream
+                .of(readOpenApiFile(servletContext, "/META-INF/openapi.json", 
JSON),
+                    readOpenApiFile(servletContext, "/META-INF/openapi.yaml", 
YAML),
+                    readOpenApiFile(servletContext, "/META-INF/openapi.yml", 
YAML))
+                .filter(Optional::isPresent)
+                .findFirst()
+                .flatMap(file -> file);
+
+            staticOpenApi.ifPresent(openAPI -> 
setOpenApi(servletContext,openAPI));
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void setOpenApi(final ServletContext servletContext, final 
OpenAPI openAPI) {
+        Objects.requireNonNull(servletContext);
+        Objects.requireNonNull(openAPI);
+        
servletContext.setAttribute(MicroProfileOpenApiRegistration.class.getName() + 
".OpenAPI", openAPI);
+    }
+
+    public static OpenAPI getOpenApi(final ServletContext servletContext) {
+        Objects.requireNonNull(servletContext);
+        return (OpenAPI) 
servletContext.getAttribute(MicroProfileOpenApiRegistration.class.getName() + 
".OpenAPI");
+    }
+
+    private Optional<OpenAPI> readOpenApiFile(
+        final ServletContext servletContext,
+        final String location,
+        final Format format)
+        throws Exception {
+
+        final URL resource = servletContext.getResource(location);
+        if (resource == null) {
+            return Optional.empty();
+        }
+
+        final OpenApiDocument document = OpenApiDocument.INSTANCE;
+        try (OpenApiStaticFile staticFile = new 
OpenApiStaticFile(resource.openStream(), format)) {
+            Config config = ConfigProvider.getConfig();
+            OpenApiConfig openApiConfig = new OpenApiConfigImpl(config);
+            document.reset();
+            document.config(openApiConfig);
+            document.filter(OpenApiProcessor.getFilter(openApiConfig, 
Thread.currentThread().getContextClassLoader()));
+            
document.modelFromStaticFile(OpenApiProcessor.modelFromStaticFile(staticFile));
+            document.initialize();
+            return Optional.of(document.get());
+        } finally {
+            document.reset();
+        }
+    }
+
+}
\ No newline at end of file
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
 
b/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
index 5d63c21c21..ccc38a2e9c 100644
--- 
a/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
@@ -1 +1,2 @@
-org.apache.tomee.microprofile.metrics.MicroProfileServletContainerInitializer
\ No newline at end of file
+org.apache.tomee.microprofile.metrics.MicroProfileMetricsRegistration
+org.apache.tomee.microprofile.openapi.MicroProfileOpenApiRegistration
\ No newline at end of file

Reply via email to