You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2018/11/30 02:27:32 UTC
[cxf] branch master updated: CXF-7749: OpenApi 3.0: Enable
restriction of swagger definitions per CXF endpoint (based on the path)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new 0861dd6 CXF-7749: OpenApi 3.0: Enable restriction of swagger definitions per CXF endpoint (based on the path)
0861dd6 is described below
commit 0861dd6eefeb15cd3f642ff6144159d047445c97
Author: reta <dr...@gmail.com>
AuthorDate: Thu Nov 1 06:52:24 2018 -0400
CXF-7749: OpenApi 3.0: Enable restriction of swagger definitions per CXF endpoint (based on the path)
---
.../description_openapi_v3_spring/README.txt | 45 ++++++
.../jax_rs/description_openapi_v3_spring/pom.xml | 102 +++++++++++++
.../demo/jaxrs/openapi/server/ApiOriginFilter.java | 45 ++++++
.../main/java/demo/jaxrs/openapi/server/Item.java | 49 +++++++
.../java/demo/jaxrs/openapi/server/Sample.java | 161 +++++++++++++++++++++
.../java/demo/jaxrs/openapi/server/Sample2.java | 120 +++++++++++++++
.../java/demo/jaxrs/openapi/server/Server.java | 39 +++++
.../resources/META-INF/cxf/org.apache.cxf.Logger | 1 +
.../src/main/resources/context.xml | 87 +++++++++++
.../src/main/resources/logback.xml | 16 ++
parent/pom.xml | 2 +-
.../apache/cxf/jaxrs/JAXRSServerFactoryBean.java | 14 +-
.../cxf/jaxrs/openapi/DelegatingServletConfig.java | 60 ++++++++
.../apache/cxf/jaxrs/openapi/OpenApiFeature.java | 33 ++++-
.../cxf/jaxrs/openapi/ServletConfigProvider.java | 54 +++++++
.../cxf/jaxrs/openapi/SyntheticServletConfig.java | 60 ++++++++
.../jaxrs/description/group2/BookStore.java | 40 +++++
.../openapi/OpenApiContextBasedConfigTest.java | 131 +++++++++++++++++
18 files changed, 1052 insertions(+), 7 deletions(-)
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/README.txt b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/README.txt
new file mode 100644
index 0000000..7c5fd49
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/README.txt
@@ -0,0 +1,45 @@
+JAX-RS Swagger2Feature Spring Demo
+=================
+
+The demo shows a basic usage of Swagger 2.0 API documentation with REST based Web Services using
+JAX-RS 2.0 (JSR-339).
+
+Building and running the demo using Maven
+---------------------------------------
+
+From the base directory of this sample (i.e., where this README file is
+located), the Maven pom.xml file can be used to build and run the demo.
+
+
+Using either UNIX or Windows:
+
+ mvn install
+ mvn -Pserver (from one command line window)
+
+
+After the service is started, the Swagger API documents in JSON and YAML
+are available at
+
+ http://localhost:9000/sample/openapi.json
+ http://localhost:9000/sample/openapi.yaml
+
+ http://localhost:9000/sample2/openapi.json
+ http://localhost:9000/sample2/openapi.yaml
+
+To remove the target dir, run mvn clean".
+
+
+If you do not have your swagger-ui on your local system, you can download
+a copy from its download site.
+
+At the console, type
+
+ wget -N https://github.com/swagger-api/swagger-ui/archive/master.zip
+ unzip master.zip
+
+This will extract the content of the swagger-ui zip file. Using your Browser, open
+the index.html file at swagger-ui-master/dist/. Finally, type in the above swagger
+document URL in the input field and click on "Explore" to view the document.
+
+
+
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/pom.xml b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/pom.xml
new file mode 100644
index 0000000..b6c6773
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>jax_rs_description_openapi_v3_spring</artifactId>
+ <name>JAX-RS Swagger2 Spring Demo</name>
+ <description>JAX-RS Swagger2 Basic Demo using Spring</description>
+ <parent>
+ <groupId>org.apache.cxf.samples</groupId>
+ <artifactId>cxf-samples</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+
+ <properties>
+ <cxf.version>${project.version}</cxf.version>
+ </properties>
+
+ <profiles>
+ <profile>
+ <id>server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>demo.jaxrs.openapi.server.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </dependency>
+ <!-- This dependency is needed if you're using the Jetty container -->
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-jetty</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ </dependency>
+ <!-- spring -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/ApiOriginFilter.java b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/ApiOriginFilter.java
new file mode 100644
index 0000000..e970d0a
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/ApiOriginFilter.java
@@ -0,0 +1,45 @@
+/**
+ * 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 demo.jaxrs.openapi.server;
+
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * This is a simple optional CORS filter used for this demo to make the resources accessible
+ * from other origins. You may omit using this filter or use CXF's advanced CORS filter
+ * org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter
+ * included in cxf-rt-rs-security-cors if you need a more comprehensive accessibility rules.
+ */
+public class ApiOriginFilter implements ContainerResponseFilter {
+
+ @Override
+ public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
+ throws IOException {
+ MultivaluedMap<String, Object> headers = responseContext.getHeaders();
+ headers.add("Access-Control-Allow-Origin", "*");
+ headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
+ headers.add("Access-Control-Allow-Headers", "Content-Type");
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Item.java b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Item.java
new file mode 100644
index 0000000..7a32208
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Item.java
@@ -0,0 +1,49 @@
+/**
+ * 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 demo.jaxrs.openapi.server;
+
+public class Item {
+ private String name;
+ private String value;
+
+ public Item() {
+ }
+
+ public Item(final String name, final String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Sample.java b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Sample.java
new file mode 100644
index 0000000..f07ee28
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Sample.java
@@ -0,0 +1,161 @@
+/**
+ * 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 demo.jaxrs.openapi.server;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+
+@Path("/sample")
+public class Sample {
+ private Map<String, Item> items;
+
+ public Sample() {
+ items = Collections.synchronizedMap(new TreeMap<String, Item>(String.CASE_INSENSITIVE_ORDER));
+ items.put("Item 1", new Item("Item 1", "Value 1"));
+ items.put("Item 2", new Item("Item 2", "Value 2"));
+ }
+
+ @Produces({ MediaType.APPLICATION_JSON })
+ @GET
+ @Operation(
+ summary = "Get all items",
+ description = "Get operation with Response and @Default value",
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema(schema = @Schema(implementation = Item.class))),
+ responseCode = "200"
+ )
+ }
+ )
+ public Response getItems(@Parameter(required = true) @QueryParam("page") @DefaultValue("1") int page) {
+ return Response.ok(items.values()).build();
+ }
+
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Path("/{name}")
+ @GET
+ @Operation(
+ summary = "Get item by name",
+ description = "Get operation with type and headers",
+ responses = {
+ @ApiResponse(content = @Content(schema = @Schema(implementation = Item.class)), responseCode = "200"),
+ @ApiResponse(responseCode = "404")
+ }
+ )
+ public Response getItem(
+ @Parameter(required = true) @HeaderParam("Accept-Language") final String language,
+ @Parameter(required = true) @PathParam("name") String name) {
+ return items.containsKey(name)
+ ? Response.ok().entity(items.get(name)).build()
+ : Response.status(Status.NOT_FOUND).build();
+ }
+
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @POST
+ @Operation(
+ summary = "Create new item",
+ description = "Post operation with entity in a body",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(implementation = Item.class),
+ mediaType = MediaType.APPLICATION_JSON
+ ),
+ headers = @Header(name = "Location"),
+ responseCode = "201"
+ )
+ }
+ )
+ public Response createItem(
+ @Context final UriInfo uriInfo,
+ @Parameter(required = true) final Item item) {
+ items.put(item.getName(), item);
+ return Response
+ .created(uriInfo.getBaseUriBuilder().path(item.getName()).build())
+ .entity(item).build();
+ }
+
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Path("/{name}")
+ @PUT
+ @Operation(
+ summary = "Update an existing new item",
+ description = "Put operation with form parameter",
+ responses = {
+ @ApiResponse(
+ content = @Content(schema = @Schema(implementation = Item.class)),
+ responseCode = "200"
+ )
+ }
+ )
+ public Item updateItem(
+ @Parameter(required = true) @PathParam("name") String name,
+ @Parameter(required = true) @FormParam("value") String value) {
+ Item item = new Item(name, value);
+ items.put(name, item);
+ return item;
+ }
+
+ @Path("/{name}")
+ @DELETE
+ @Operation(
+ summary = "Delete an existing new item",
+ description = "Delete operation with implicit header",
+ responses = @ApiResponse(responseCode = "204")
+ )
+ @Parameter(
+ name = "Accept-Language",
+ description = "language",
+ required = true,
+ schema = @Schema(implementation = String.class),
+ in = ParameterIn.HEADER
+ )
+ public void delete(@Parameter(required = true) @PathParam("name") String name) {
+ items.remove(name);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Sample2.java b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Sample2.java
new file mode 100644
index 0000000..b3e4c9c
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Sample2.java
@@ -0,0 +1,120 @@
+/**
+ * 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 demo.jaxrs.openapi.server;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.core.Response.Status;
+
+import demo.jaxrs.openapi.server.Item;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+
+@Path("/sample2")
+public class Sample2 {
+ private Map<String, Item> items;
+
+ public Sample2() {
+ items = Collections.synchronizedMap(new TreeMap<String, Item>(String.CASE_INSENSITIVE_ORDER));
+ items.put("Item 1", new Item("Item 1", "Value 1"));
+ items.put("Item 2", new Item("Item 2", "Value 2"));
+ }
+
+
+ @Produces({ MediaType.APPLICATION_JSON })
+ @GET
+ @Operation(
+ summary = "Get all items",
+ description = "Get operation with Response and @Default value",
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema(schema = @Schema(implementation = Item.class))),
+ responseCode = "200"
+ )
+ }
+ )
+ public Response getItems(@Parameter(required = true) @QueryParam("page") @DefaultValue("1") int page) {
+ return Response.ok(items.values()).build();
+ }
+
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Path("/{name}")
+ @GET
+ @Operation(
+ summary = "Get item by name",
+ description = "Get operation with type and headers",
+ responses = {
+ @ApiResponse(content = @Content(schema = @Schema(implementation = Item.class)), responseCode = "200"),
+ @ApiResponse(responseCode = "404")
+ }
+ )
+ public Response getItem(
+ @Parameter(required = true) @HeaderParam("Accept-Language") final String language,
+ @Parameter(required = true) @PathParam("name") String name) {
+ return items.containsKey(name)
+ ? Response.ok().entity(items.get(name)).build()
+ : Response.status(Status.NOT_FOUND).build();
+ }
+
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @POST
+ @Operation(
+ summary = "Create new item",
+ description = "Post operation with entity in a body",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(implementation = Item.class),
+ mediaType = MediaType.APPLICATION_JSON
+ ),
+ headers = @Header(name = "Location"),
+ responseCode = "201"
+ )
+ }
+ )
+ public Response createItem(
+ @Context final UriInfo uriInfo,
+ @Parameter(required = true) final Item item) {
+ items.put(item.getName(), item);
+ return Response
+ .created(uriInfo.getBaseUriBuilder().path(item.getName()).build())
+ .entity(item).build();
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Server.java b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Server.java
new file mode 100644
index 0000000..b8ed00b
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/java/demo/jaxrs/openapi/server/Server.java
@@ -0,0 +1,39 @@
+/**
+ * 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 demo.jaxrs.openapi.server;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public final class Server {
+ private Server() {
+ }
+
+ public static void main(String[] args) throws Exception {
+ ApplicationContext appctxt =
+ new ClassPathXmlApplicationContext(Server.class.getResource("/context.xml").toString());
+
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 6000 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/META-INF/cxf/org.apache.cxf.Logger b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
new file mode 100644
index 0000000..27dd788
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
@@ -0,0 +1 @@
+org.apache.cxf.common.logging.Slf4jLogger
\ No newline at end of file
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/context.xml b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/context.xml
new file mode 100644
index 0000000..73aeb15
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/context.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!-- START SNIPPET: beans -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"
+ xmlns:jaxrs="http://cxf.apache.org/jaxrs"
+ xsi:schemaLocation="http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
+ http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <import resource="classpath:META-INF/cxf/cxf.xml" />
+
+ <!-- JAXRS providers -->
+ <bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
+ <bean id="multipartProvider" class="org.apache.cxf.jaxrs.provider.MultipartProvider" />
+ <bean id="originFilter" class="demo.jaxrs.openapi.server.ApiOriginFilter" />
+
+ <!-- Application resources -->
+ <bean id="sampleResource" class="demo.jaxrs.openapi.server.Sample" />
+ <bean id="sampleResource2" class="demo.jaxrs.openapi.server.Sample2" />
+
+ <!-- CXF OpenApiFeature -->
+ <bean id="openApiFeature" class="org.apache.cxf.jaxrs.openapi.OpenApiFeature">
+ <property name="title" value="Sample REST Application" />
+ <property name="useContextBasedConfig" value="true" />
+ <property name="scan" value="false" />
+ </bean>
+
+ <bean id="openApiFeature2" class="org.apache.cxf.jaxrs.openapi.OpenApiFeature">
+ <property name="title" value="Sample2 REST Application" />
+ <property name="useContextBasedConfig" value="true" />
+ <property name="scan" value="false" />
+ </bean>
+
+ <cxf:bus>
+ <cxf:features>
+ <cxf:logging />
+ </cxf:features>
+ </cxf:bus>
+
+ <jaxrs:server id="sampleServer" address="http://localhost:9000/sample">
+ <jaxrs:serviceBeans>
+ <ref bean="sampleResource" />
+ </jaxrs:serviceBeans>
+ <jaxrs:providers>
+ <ref bean="jsonProvider" />
+ <ref bean="multipartProvider" />
+ <ref bean="originFilter" />
+ </jaxrs:providers>
+ <jaxrs:features>
+ <ref bean="openApiFeature" />
+ </jaxrs:features>
+ </jaxrs:server>
+
+ <jaxrs:server id="sampleServer2" address="http://localhost:9000/sample2">
+ <jaxrs:serviceBeans>
+ <ref bean="sampleResource2" />
+ </jaxrs:serviceBeans>
+ <jaxrs:providers>
+ <ref bean="jsonProvider" />
+ <ref bean="multipartProvider" />
+ <ref bean="originFilter" />
+ </jaxrs:providers>
+ <jaxrs:features>
+ <ref bean="openApiFeature2" />
+ </jaxrs:features>
+ </jaxrs:server>
+
+</beans>
+<!-- END SNIPPET: beans -->
diff --git a/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/logback.xml b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/logback.xml
new file mode 100644
index 0000000..59bce12
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/description_openapi_v3_spring/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="5 seconds">
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>[%level] %d{yyyy-MM-dd HH:mm:ss.SSS} %logger{36} - [%X] %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+ <logger category="io.swagger" name="swagger">
+ <level name="DEBUG"/>
+ </logger>
+</configuration>
diff --git a/parent/pom.xml b/parent/pom.xml
index e3a7cad..0891a7a 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -164,7 +164,7 @@
<cxf.spring.ldap.version>2.3.2.RELEASE</cxf.spring.ldap.version>
<cxf.spring.mock>spring-test</cxf.spring.mock>
<cxf.swagger2.version>1.5.17</cxf.swagger2.version><!-- updating to 1.5.18 will cause systest failures -->
- <cxf.swagger.v3.version>2.0.5</cxf.swagger.v3.version>
+ <cxf.swagger.v3.version>2.0.6</cxf.swagger.v3.version>
<cxf.swagger.ui.version>3.20.0</cxf.swagger.ui.version>
<cxf.tomcat.version>8.0.53</cxf.tomcat.version>
<cxf.velocity.version>2.0</cxf.velocity.version>
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java
index 6ea0632..8548865 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java
@@ -194,7 +194,7 @@ public class JAXRSServerFactoryBean extends AbstractJAXRSFactoryBean {
applyFeatures();
updateClassResourceProviders(ep);
- injectContexts(factory);
+ injectContexts(factory, (ApplicationInfo)ep.get(Application.class.getName()));
factory.applyDynamicFeatures(getServiceFactory().getClassResourceInfo());
@@ -403,8 +403,14 @@ public class JAXRSServerFactoryBean extends AbstractJAXRSFactoryBean {
this.start = start;
}
- protected void injectContexts(ServerProviderFactory factory) {
- Application application = appProvider == null ? null : appProvider.getProvider();
+ protected void injectContexts(ServerProviderFactory factory, ApplicationInfo fallback) {
+ // Sometimes the application provider (ApplicationInfo) is injected through
+ // the endpoint, not JAXRSServerFactoryBean (like for example OpenApiFeature
+ // or Swagger2Feature do). As such, without consulting the endpoint, the injection
+ // may not work properly.
+ final ApplicationInfo appInfoProvider = (appProvider == null) ? fallback : appProvider;
+ final Application application = appInfoProvider == null ? null : appInfoProvider.getProvider();
+
for (ClassResourceInfo cri : serviceFactory.getClassResourceInfo()) {
if (cri.isSingleton()) {
InjectionUtils.injectContextProxiesAndApplication(cri,
@@ -414,7 +420,7 @@ public class JAXRSServerFactoryBean extends AbstractJAXRSFactoryBean {
}
}
if (application != null) {
- InjectionUtils.injectContextProxiesAndApplication(appProvider,
+ InjectionUtils.injectContextProxiesAndApplication(appInfoProvider,
application, null, null);
}
}
diff --git a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/DelegatingServletConfig.java b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/DelegatingServletConfig.java
new file mode 100644
index 0000000..6233cf3
--- /dev/null
+++ b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/DelegatingServletConfig.java
@@ -0,0 +1,60 @@
+/**
+ * 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.cxf.jaxrs.openapi;
+
+import java.util.Enumeration;
+import java.util.Objects;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import io.swagger.v3.oas.integration.api.OpenApiContext;
+
+class DelegatingServletConfig implements ServletConfig {
+ private final ServletConfig delegate;
+ private final String contextId;
+
+ DelegatingServletConfig(final ServletConfig sc, String contextId) {
+ this.delegate = sc;
+ this.contextId = contextId;
+ }
+
+ @Override
+ public String getServletName() {
+ return delegate.getServletName();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return delegate.getServletContext();
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return delegate.getInitParameterNames();
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ if (Objects.equals(OpenApiContext.OPENAPI_CONTEXT_ID_KEY, name)) {
+ return contextId;
+ }
+ return delegate.getInitParameter(name);
+ }
+}
diff --git a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
index df7000e..25a06a7 100644
--- a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
+++ b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
import javax.ws.rs.core.Application;
@@ -104,6 +105,11 @@ public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport,
private boolean scanKnownConfigLocations = true;
// Swagger UI configuration parameters (to be passed as query string).
private SwaggerUiConfig swaggerUiConfig;
+ // Generates the Swagger Context ID (instead of using the default one). It is
+ // necessary when more than one JAXRS Server Factory Bean or OpenApiFeature instance
+ // are co-located in the same application.
+ private boolean useContextBasedConfig;
+ private String ctxId;
protected static class DefaultApplication extends Application {
@@ -135,6 +141,11 @@ public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport,
if (resourcePackages != null) {
packages.addAll(resourcePackages);
}
+
+ // Generate random Context ID for Swagger
+ if (useContextBasedConfig) {
+ ctxId = UUID.randomUUID().toString();
+ }
Properties swaggerProps = null;
GenericOpenApiContextBuilder<?> openApiConfiguration;
@@ -166,11 +177,13 @@ public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport,
openApiConfiguration = new JaxrsOpenApiContextBuilder<>()
.application(application)
- .openApiConfiguration(config);
+ .openApiConfiguration(config)
+ .ctxId(ctxId); /* will be null if not used */
} else {
openApiConfiguration = new JaxrsOpenApiContextBuilder<>()
.application(application)
- .configLocation(defaultConfigLocation);
+ .configLocation(defaultConfigLocation)
+ .ctxId(ctxId); /* will be null if not used */
}
try {
@@ -182,6 +195,10 @@ public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport,
registerOpenApiResources(sfb, packages, context.getOpenApiConfiguration());
registerSwaggerUiResources(sfb, combine(swaggerProps, userProperties), factory, bus);
+ if (useContextBasedConfig) {
+ registerServletConfigProvider(factory);
+ }
+
if (customizer != null) {
customizer.setApplicationInfo(factory.getApplicationProvider());
}
@@ -405,6 +422,14 @@ public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport,
this.swaggerUiConfig = swaggerUiConfig;
}
+ public void setUseContextBasedConfig(final boolean useContextBasedConfig) {
+ this.useContextBasedConfig = useContextBasedConfig;
+ }
+
+ public boolean isUseContextBasedConfig() {
+ return useContextBasedConfig;
+ }
+
@Override
public SwaggerUiConfig getSwaggerUiConfig() {
return swaggerUiConfig;
@@ -445,6 +470,10 @@ public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport,
.resourcePackages(packages)));
}
+ protected void registerServletConfigProvider(ServerProviderFactory factory) {
+ factory.setUserProviders(Arrays.asList(new ServletConfigProvider(ctxId)));
+ }
+
protected void registerSwaggerUiResources(JAXRSServiceFactoryBean sfb, Properties properties,
ServerProviderFactory factory, Bus bus) {
diff --git a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/ServletConfigProvider.java b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/ServletConfigProvider.java
new file mode 100644
index 0000000..757691c
--- /dev/null
+++ b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/ServletConfigProvider.java
@@ -0,0 +1,54 @@
+/**
+ * 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.cxf.jaxrs.openapi;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.cxf.jaxrs.ext.ContextProvider;
+import org.apache.cxf.message.Message;
+
+class ServletConfigProvider implements ContextProvider<ServletConfig> {
+ private final String contextId;
+
+ ServletConfigProvider(String contextId) {
+ this.contextId = contextId;
+ }
+
+ @Override
+ public ServletConfig createContext(Message message) {
+ final ServletConfig sc = (ServletConfig)message.get("HTTP.CONFIG");
+
+ // When deploying into OSGi container, it is possible to use embedded Jetty
+ // transport. In this case, the ServletConfig is not available and Swagger
+ // does not take into account certain configuration parameters. To overcome
+ // that, the ServletConfig is synthesized from ServletContext instance.
+ if (sc == null) {
+ final ServletContext context = (ServletContext)message.get("HTTP.CONTEXT");
+ if (context != null) {
+ return new SyntheticServletConfig(context, contextId);
+ }
+ } else {
+ return new DelegatingServletConfig(sc, contextId);
+ }
+
+ return sc;
+ }
+}
diff --git a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/SyntheticServletConfig.java b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/SyntheticServletConfig.java
new file mode 100644
index 0000000..bd0b079
--- /dev/null
+++ b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/SyntheticServletConfig.java
@@ -0,0 +1,60 @@
+/**
+ * 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.cxf.jaxrs.openapi;
+
+import java.util.Enumeration;
+import java.util.Objects;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import io.swagger.v3.oas.integration.api.OpenApiContext;
+
+class SyntheticServletConfig implements ServletConfig {
+ private final ServletContext delegate;
+ private final String contextId;
+
+ SyntheticServletConfig(final ServletContext context, String contextId) {
+ this.delegate = context;
+ this.contextId = contextId;
+ }
+
+ @Override
+ public String getServletName() {
+ return null; /* no servlet available */
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return delegate;
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return delegate.getInitParameterNames();
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ if (Objects.equals(OpenApiContext.OPENAPI_CONTEXT_ID_KEY, name)) {
+ return contextId;
+ }
+ return delegate.getInitParameter(name);
+ }
+}
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group2/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group2/BookStore.java
new file mode 100644
index 0000000..c9c87ab
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group2/BookStore.java
@@ -0,0 +1,40 @@
+/**
+ * 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.cxf.systest.jaxrs.description.group2;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.systest.jaxrs.Book;
+
+@Path("/")
+public class BookStore {
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/books")
+ @POST
+ public Response post(Book book) {
+ return Response.ok().build();
+ }
+}
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/OpenApiContextBasedConfigTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/OpenApiContextBasedConfigTest.java
new file mode 100644
index 0000000..f38773b
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/OpenApiContextBasedConfigTest.java
@@ -0,0 +1,131 @@
+/**
+ * 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.cxf.systest.jaxrs.description.openapi;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+
+import org.apache.cxf.ext.logging.LoggingFeature;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+import org.apache.cxf.jaxrs.model.UserApplication;
+import org.apache.cxf.jaxrs.openapi.OpenApiCustomizer;
+import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
+import org.apache.cxf.jaxrs.openapi.parse.OpenApiParseUtils;
+import org.apache.cxf.systest.jaxrs.description.group2.BookStore;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+public class OpenApiContextBasedConfigTest extends AbstractBusClientServerTestBase {
+ private static final String PORT = allocatePort(OpenApiContextBasedConfigTest.class);
+
+ public static class OpenApiContextBased extends AbstractBusTestServerBase {
+ @Override
+ protected void run() {
+ createServerFactory("/api", "This is first API (api)", BookStoreOpenApi.class);
+ createServerFactory("/api2", "This is second API (api2)", BookStore.class);
+ }
+
+ protected void createServerFactory(final String context, final String description, final Class<?> resource) {
+ final JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(resource);
+ sf.setProvider(new JacksonJsonProvider());
+ final OpenApiFeature feature = createOpenApiFeature(description, resource);
+ sf.setFeatures(Arrays.asList(feature));
+ sf.setAddress("http://localhost:" + PORT + context);
+ sf.create();
+ }
+
+
+ protected OpenApiFeature createOpenApiFeature(final String description, final Class<?> resource) {
+ final OpenApiCustomizer customizer = new OpenApiCustomizer();
+ customizer.setDynamicBasePath(true);
+
+ final OpenApiFeature feature = new OpenApiFeature();
+ feature.setDescription(description);
+ feature.setCustomizer(customizer);
+ feature.setScan(false);
+ feature.setUseContextBasedConfig(true);
+ feature.setResourceClasses(Collections.singleton(resource.getName()));
+ return feature;
+ }
+
+ public static void main(String[] args) {
+ try {
+ new OpenApiContextBased().start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+ }
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ //keep out of process due to stack traces testing failures
+ assertTrue("server did not launch correctly", launchServer(OpenApiContextBased.class, false));
+ createStaticBus();
+ }
+
+ @Test
+ public void testFirstApi() {
+ final String swaggerJson = createWebClient("http://localhost:" + PORT + "/api/openapi.json").get(String.class);
+ assertThat(swaggerJson, containsString("This is first API (api)"));
+
+ final UserApplication ap = OpenApiParseUtils.getUserApplicationFromJson(swaggerJson);
+ assertNotNull(ap);
+ assertThat(ap.getResourcesAsMap().size(), equalTo(1));
+ assertThat(ap.getResourcesAsMap().get("").getOperations().size(), equalTo(3));
+ }
+
+ @Test
+ public void testSecondApi() {
+ final String swaggerJson = createWebClient("http://localhost:" + PORT + "/api2/openapi.json").get(String.class);
+ assertThat(swaggerJson, containsString("This is second API (api2)"));
+
+ final UserApplication ap = OpenApiParseUtils.getUserApplicationFromJson(swaggerJson);
+ assertNotNull(ap);
+ assertThat(ap.getResourcesAsMap().size(), equalTo(1));
+ assertThat(ap.getResourcesAsMap().get("").getOperations().size(), equalTo(1));
+ }
+
+ protected WebClient createWebClient(final String url) {
+ return WebClient
+ .create(url,
+ Arrays.< Object >asList(new JacksonJsonProvider()),
+ Arrays.< Feature >asList(new LoggingFeature()),
+ null)
+ .accept(MediaType.APPLICATION_JSON).accept("application/yaml");
+ }
+}