diff --git a/examples/mp-openapi-reader/pom.xml b/examples/mp-openapi-reader/pom.xml
new file mode 100644
index 00000000000..95ae07fc7e8
--- /dev/null
+++ b/examples/mp-openapi-reader/pom.xml
@@ -0,0 +1,127 @@
+
+
+
+ 4.0.0
+ org.superbiz
+ mp-openapi-reader
+ 10.0.0-SNAPSHOT
+ war
+ TomEE :: Examples :: Microprofile OpenAPI Reader
+
+ 9.1.1
+ 1.7.0.Final
+ 4.13.2
+ 10.0.0-SNAPSHOT
+ 3.0.1
+ 3.1.1
+
+
+
+ org.apache.tomee
+ jakartaee-api
+ ${version.jakartaee-api}
+ provided
+
+
+ org.eclipse.microprofile.openapi
+ microprofile-openapi-api
+ ${version.openapi-api}
+
+
+ io.smallrye
+ smallrye-open-api
+ ${version.microprofile.impl.openapi}
+ provided
+ pom
+
+
+ io.smallrye
+ smallrye-open-api-core
+ ${version.microprofile.impl.openapi}
+ provided
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.apache.tomee
+ openejb-cxf-rs
+ ${tomee.version}
+ test
+
+
+ org.jboss.arquillian.junit
+ arquillian-junit-container
+ ${version.arquillian.bom}
+ test
+
+
+ org.apache.tomee
+ arquillian-tomee-remote
+ ${tomee.version}
+ test
+
+
+ org.apache.tomee
+ apache-tomee
+ ${tomee.version}
+ zip
+ microprofile
+ test
+
+
+
+
+
+ org.apache.tomee.maven
+ tomee-maven-plugin
+ ${tomee.version}
+
+ microprofile
+ ${project.artifactId}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.7.0
+
+
+ 1.8
+
+
+
+
+
+
+
+
+ localhost
+ file://${basedir}/target/repo/
+
+
+ localhost
+ file://${basedir}/target/snapshot-repo/
+
+
+
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java
new file mode 100644
index 00000000000..2dc481d8380
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java
@@ -0,0 +1,77 @@
+package org.superbiz.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 org.eclipse.microprofile.config.ConfigProvider;
+import org.eclipse.microprofile.openapi.OASModelReader;
+import org.eclipse.microprofile.openapi.models.OpenAPI;
+
+import java.net.URL;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import static io.smallrye.openapi.runtime.io.Format.JSON;
+import static io.smallrye.openapi.runtime.io.Format.YAML;
+
+public class CustomReader implements OASModelReader, ServletContainerInitializer {
+
+ private static final Logger LOGGER = Logger.getLogger(CustomReader.class.getName());
+
+ private static Optional openAPI;
+
+ @Override
+ public OpenAPI buildModel() {
+ return openAPI.orElseThrow(() -> new IllegalStateException("OpenAPI document not available."));
+ }
+
+ @Override
+ public void onStartup(final Set> c, final ServletContext ctx) throws ServletException {
+ try {
+ final OpenApiConfig openApiConfig = new OpenApiConfigImpl(ConfigProvider.getConfig());;
+ final Optional yaml = readOpenApiFile(ctx, "/WEB-INF/classes/openapi/openapi.yaml", YAML);
+ final Optional json = readOpenApiFile(ctx, "/WEB-INF/classes/my-openapi.json", JSON);
+
+ final OpenApiDocument document = OpenApiDocument.INSTANCE;
+ try {
+ document.reset();
+ document.config(openApiConfig);
+ yaml.ifPresent(document::modelFromStaticFile);
+ json.ifPresent(document::modelFromStaticFile);
+ document.initialize();
+
+ openAPI = Optional.ofNullable(document.get());
+
+ } finally {
+ document.reset();
+ }
+
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Optional readOpenApiFile(
+ final ServletContext servletContext, final String location,
+ final Format format) throws Exception {
+
+ final URL resource = servletContext.getResource(location);
+ if (resource == null) {
+ LOGGER.fine("Could not find static OpenAPI file " + location);
+ return Optional.empty();
+ }
+
+ LOGGER.fine("Found static OpenAPI file " + location);
+
+ try (OpenApiStaticFile staticFile = new OpenApiStaticFile(resource.openStream(), format)) {
+ return Optional.of(OpenApiProcessor.modelFromStaticFile(staticFile));
+ }
+ }
+}
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java
new file mode 100644
index 00000000000..bc6df1889d4
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java
@@ -0,0 +1,37 @@
+package org.superbiz.microprofile.openapi; /**
+ * 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.
+ */
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+@Path("/weather")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+@ApplicationScoped
+public class WeatherService {
+
+ @Path("/day/status")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String dayStatus() {
+ return "Hi, today is a sunny day!";
+ }
+}
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java
new file mode 100644
index 00000000000..5bf82643174
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java
@@ -0,0 +1,102 @@
+/**
+ * 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.superbiz.microprofile.openapi.moviefun;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.xml.bind.annotation.XmlRootElement;
+
+@Entity
+@XmlRootElement(name = "movie")
+public class Movie {
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long id;
+
+ private String director;
+ private String title;
+ private int year;
+ private String genre;
+ private int rating;
+
+ public Movie() {
+ }
+
+ public Movie(String title, String director, String genre, int rating, int year) {
+ this.director = director;
+ this.title = title;
+ this.year = year;
+ this.genre = genre;
+ this.rating = rating;
+ }
+
+ public Movie(String director, String title, int year) {
+ this.director = director;
+ this.title = title;
+ this.year = year;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getDirector() {
+ return director;
+ }
+
+ public void setDirector(String director) {
+ this.director = director;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public int getYear() {
+ return year;
+ }
+
+ public void setYear(int year) {
+ this.year = year;
+ }
+
+ public String getGenre() {
+ return genre;
+ }
+
+ public void setGenre(String genre) {
+ this.genre = genre;
+ }
+
+ public int getRating() {
+ return rating;
+ }
+
+ public void setRating(int rating) {
+ this.rating = rating;
+ }
+}
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java
new file mode 100644
index 00000000000..803e53f40c2
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java
@@ -0,0 +1,92 @@
+/**
+ * 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.superbiz.microprofile.openapi.moviefun;
+
+import jakarta.ejb.Stateless;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.TypedQuery;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import jakarta.persistence.metamodel.EntityType;
+
+import java.util.List;
+
+@Stateless
+public class MoviesBean {
+
+ @PersistenceContext(unitName = "movie-unit")
+ private EntityManager entityManager;
+
+ public Movie find(Long id) {
+ return entityManager.find(Movie.class, id);
+ }
+
+ public void addMovie(Movie movie) {
+ entityManager.persist(movie);
+ }
+
+ public void editMovie(Movie movie) {
+ entityManager.merge(movie);
+ }
+
+ public void deleteMovie(long id) {
+ Movie movie = entityManager.find(Movie.class, id);
+ entityManager.remove(movie);
+ }
+
+ public List getMovies(Integer firstResult, Integer maxResults, String field, String searchTerm) {
+ CriteriaBuilder qb = entityManager.getCriteriaBuilder();
+ CriteriaQuery cq = qb.createQuery(Movie.class);
+ Root root = cq.from(Movie.class);
+ EntityType type = entityManager.getMetamodel().entity(Movie.class);
+ if (field != null && searchTerm != null && !"".equals(field.trim()) && !"".equals(searchTerm.trim())) {
+ Path path = root.get(type.getDeclaredSingularAttribute(field.trim(), String.class));
+ Predicate condition = qb.like(path, "%" + searchTerm.trim() + "%");
+ cq.where(condition);
+ }
+ TypedQuery q = entityManager.createQuery(cq);
+ if (maxResults != null) {
+ q.setMaxResults(maxResults);
+ }
+ if (firstResult != null) {
+ q.setFirstResult(firstResult);
+ }
+ return q.getResultList();
+ }
+
+ public int count(String field, String searchTerm) {
+ CriteriaBuilder qb = entityManager.getCriteriaBuilder();
+ CriteriaQuery cq = qb.createQuery(Long.class);
+ Root root = cq.from(Movie.class);
+ EntityType type = entityManager.getMetamodel().entity(Movie.class);
+ cq.select(qb.count(root));
+ if (field != null && searchTerm != null && !"".equals(field.trim()) && !"".equals(searchTerm.trim())) {
+ Path path = root.get(type.getDeclaredSingularAttribute(field.trim(), String.class));
+ Predicate condition = qb.like(path, "%" + searchTerm.trim() + "%");
+ cq.where(condition);
+ }
+ return entityManager.createQuery(cq).getSingleResult().intValue();
+ }
+
+ public void clean() {
+ entityManager.createQuery("delete from Movie").executeUpdate();
+ }
+}
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java
new file mode 100644
index 00000000000..625515c3fe9
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java
@@ -0,0 +1,34 @@
+/**
+ * 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.superbiz.microprofile.openapi.moviefun.rest;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@ApplicationPath("/rest")
+public class ApplicationConfig extends Application {
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Set> getClasses() {
+ return new HashSet>(Arrays.asList(LoadRest.class, MoviesRest.class));
+ }
+}
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java
new file mode 100644
index 00000000000..2d5d1e6e679
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java
@@ -0,0 +1,41 @@
+/**
+ * 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.superbiz.microprofile.openapi.moviefun.rest;
+
+import jakarta.ejb.EJB;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import org.superbiz.microprofile.openapi.moviefun.Movie;
+import org.superbiz.microprofile.openapi.moviefun.MoviesBean;
+
+@Path("load")
+public class LoadRest {
+ @EJB
+ private MoviesBean moviesBean;
+
+ @POST
+ public void load() {
+ moviesBean.addMovie(new Movie("Wedding Crashers", "David Dobkin", "Comedy", 7, 2005));
+ moviesBean.addMovie(new Movie("Starsky & Hutch", "Todd Phillips", "Action", 6, 2004));
+ moviesBean.addMovie(new Movie("Shanghai Knights", "David Dobkin", "Action", 6, 2003));
+ moviesBean.addMovie(new Movie("I-Spy", "Betty Thomas", "Adventure", 5, 2002));
+ moviesBean.addMovie(new Movie("The Royal Tenenbaums", "Wes Anderson", "Comedy", 8, 2001));
+ moviesBean.addMovie(new Movie("Zoolander", "Ben Stiller", "Comedy", 6, 2001));
+ moviesBean.addMovie(new Movie("Shanghai Noon", "Tom Dey", "Comedy", 7, 2000));
+ }
+
+}
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java
new file mode 100644
index 00000000000..80d2e889bf9
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java
@@ -0,0 +1,82 @@
+/**
+ * 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.superbiz.microprofile.openapi.moviefun.rest;
+
+import jakarta.ejb.EJB;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import org.superbiz.microprofile.openapi.moviefun.Movie;
+import org.superbiz.microprofile.openapi.moviefun.MoviesBean;
+
+import java.util.List;
+
+@Path("movies")
+@Produces({"application/json"})
+public class MoviesRest {
+
+ @EJB
+ private MoviesBean service;
+
+ @GET
+ @Path("{id}")
+ public Movie find(@PathParam("id") Long id) {
+ return service.find(id);
+ }
+
+ @GET
+ public List getMovies(@QueryParam("first") Integer first, @QueryParam("max") Integer max,
+ @QueryParam("field") String field, @QueryParam("searchTerm") String searchTerm) {
+ return service.getMovies(first, max, field, searchTerm);
+ }
+
+ @POST
+ @Consumes("application/json")
+ public Movie addMovie(Movie movie) {
+ service.addMovie(movie);
+ return movie;
+ }
+
+ @PUT
+ @Path("{id}")
+ @Consumes("application/json")
+ public Movie editMovie(Movie movie) {
+ service.editMovie(movie);
+ return movie;
+ }
+
+ @DELETE
+ @Path("{id}")
+ public void deleteMovie(@PathParam("id") long id) {
+ service.deleteMovie(id);
+ }
+
+ @GET
+ @Path("count")
+ @Produces(MediaType.TEXT_PLAIN)
+ public int count(@QueryParam("field") String field, @QueryParam("searchTerm") String searchTerm) {
+ return service.count(field, searchTerm);
+ }
+
+}
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties b/examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties
new file mode 100644
index 00000000000..268e2ad12cb
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties
@@ -0,0 +1,2 @@
+mp.openapi.model.reader=org.superbiz.microprofile.openapi.CustomReader
+mp.openapi.scan.disable=false
diff --git a/examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml b/examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 00000000000..ec38aaa1170
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ movieDatabase
+ movieDatabaseUnmanaged
+ org.superbiz.moviefun.Movie
+
+
+
+
+
+
diff --git a/examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
new file mode 100644
index 00000000000..d0eefa40f3b
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
@@ -0,0 +1 @@
+org.superbiz.microprofile.openapi.CustomReader
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml b/examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000000..6e1611111fe
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ Microprofile OpenAPI Reader
+
diff --git a/examples/mp-openapi-reader/src/main/webapp/my-openapi.json b/examples/mp-openapi-reader/src/main/webapp/my-openapi.json
new file mode 100644
index 00000000000..21cefed3762
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/webapp/my-openapi.json
@@ -0,0 +1,25 @@
+{
+ "openapi" : "3.0.3",
+ "info" : {
+ "title" : "Generated API",
+ "version" : "1.0"
+ },
+ "paths" : {
+ "/rest/weather/day/status" : {
+ "get" : {
+ "responses" : {
+ "200" : {
+ "description" : "OK",
+ "content" : {
+ "text/plain" : {
+ "schema" : {
+ "type" : "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml b/examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml
new file mode 100644
index 00000000000..53fb4a7fe9c
--- /dev/null
+++ b/examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml
@@ -0,0 +1,135 @@
+---
+openapi: 3.0.3
+info:
+ title: Generated API
+ version: "1.0"
+paths:
+ /rest/load:
+ post:
+ responses:
+ "201":
+ description: Created
+ /rest/movies:
+ get:
+ parameters:
+ - name: field
+ in: query
+ schema:
+ type: string
+ - name: first
+ in: query
+ schema:
+ format: int32
+ type: integer
+ - name: max
+ in: query
+ schema:
+ format: int32
+ type: integer
+ - name: searchTerm
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Movie'
+ post:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Movie'
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Movie'
+ /rest/movies/count:
+ get:
+ parameters:
+ - name: field
+ in: query
+ schema:
+ type: string
+ - name: searchTerm
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ text/plain:
+ schema:
+ format: int32
+ type: integer
+ /rest/movies/{id}:
+ get:
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int64
+ type: integer
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Movie'
+ put:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Movie'
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Movie'
+ delete:
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int64
+ type: integer
+ responses:
+ "204":
+ description: No Content
+components:
+ schemas:
+ Movie:
+ type: object
+ properties:
+ id:
+ format: int64
+ type: integer
+ director:
+ type: string
+ title:
+ type: string
+ year:
+ format: int32
+ type: integer
+ genre:
+ type: string
+ rating:
+ format: int32
+ type: integer
+ xml:
+ name: movie
\ No newline at end of file
diff --git a/examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java b/examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java
new file mode 100644
index 00000000000..01e717c7992
--- /dev/null
+++ b/examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java
@@ -0,0 +1,95 @@
+/**
+ * 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.
+ */
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+import org.apache.openejb.loader.IO;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.superbiz.microprofile.openapi.CustomReader;
+import org.superbiz.microprofile.openapi.WeatherService;
+import org.superbiz.microprofile.openapi.moviefun.Movie;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+@RunWith(Arquillian.class)
+public class WeatherServiceTest {
+
+ @Deployment(testable = false)
+ public static WebArchive createDeployment() {
+ final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war")
+ .addClass(WeatherService.class)
+ .addClass(CustomReader.class)
+ .addPackages(true, Movie.class.getPackage())
+ .addAsResource("META-INF/services/jakarta.servlet.ServletContainerInitializer")
+ .addAsResource("META-INF/persistence.xml")
+ .addAsResource("META-INF/microprofile-config.properties")
+ .addAsResource(new File("src/main/webapp/my-openapi.json"), "my-openapi.json")
+ .addAsResource(new File("src/main/webapp/openapi/openapi.yaml"), "openapi/openapi.yaml")
+ .addAsWebInfResource(new StringAsset(""), "beans.xml");
+ return webArchive;
+ }
+
+ @ArquillianResource
+ private URL base;
+
+ private Client client;
+
+ @Before
+ public void before() {
+ this.client = ClientBuilder.newClient();
+ }
+
+ @After
+ public void after() {
+ this.client.close();
+ }
+
+ @Test
+ public void openapi() throws IOException {
+ final WebTarget webTarget = client.target(base.toExternalForm());
+
+ final Response response = webTarget.path("/openapi")
+ .request()
+ .accept("application/json")
+ .buildGet().invoke();
+ final InputStream entity = (InputStream) response.getEntity();
+ final String payload = IO.slurp(entity);
+
+ // assert that it returns a payload
+ Assert.assertNotNull(payload);
+
+ // now check if the 2 openapi documents (yaml and json) got successfully loaded and merged
+ Assert.assertTrue(payload.contains("/rest/weather/day/status")); // from one openapi file
+ Assert.assertTrue(payload.contains("/rest/movies")); // from the other one
+ }
+
+}
diff --git a/examples/mp-openapi-reader/src/test/resources/arquillian.xml b/examples/mp-openapi-reader/src/test/resources/arquillian.xml
new file mode 100644
index 00000000000..0b54540a348
--- /dev/null
+++ b/examples/mp-openapi-reader/src/test/resources/arquillian.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ -1
+ -1
+ false
+ microprofile
+ target/apache-tomee-remote
+ target/arquillian-test-working-dir
+
+
+
\ No newline at end of file
diff --git a/examples/pom.xml b/examples/pom.xml
index f126a11a8e8..b5af5082d37 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -129,6 +129,7 @@
mp-rest-jwt-public-key
mp-rest-client
mp-custom-healthcheck
+ mp-openapi-reader
mtom
multi-jpa-provider-testing
multiple-arquillian-adapters