[CXF-6960] Service SwaggerUI resources if it is available on the classpath, can be disabled
diff --git a/distribution/src/main/release/samples/jax_rs/description_swagger2/README.txt b/distribution/src/main/release/samples/jax_rs/description_swagger2/README.txt
index 0a37472..af5a737 100644
--- a/distribution/src/main/release/samples/jax_rs/description_swagger2/README.txt
+++ b/distribution/src/main/release/samples/jax_rs/description_swagger2/README.txt
@@ -27,14 +27,17 @@
To view the Swagger document using Swagger-UI, use your Browser to
open the Swagger-UI page at
- http://localhost:9000/?url=/swagger.json
+ http://localhost:9000/api-docs?url=/swagger.json
or
- http://localhost:9000/?url=/swagger.yaml
+ http://localhost:9000/api-docs?url=/swagger.yaml
+or access it from the CXF Services page:
-To remove the target dir, run mvn clean".
+ http://localhost:9000/services
+ and follow a Swagger link.
+
diff --git a/distribution/src/main/release/samples/jax_rs/description_swagger2/pom.xml b/distribution/src/main/release/samples/jax_rs/description_swagger2/pom.xml
index 9c9397e..9afb7c3 100644
--- a/distribution/src/main/release/samples/jax_rs/description_swagger2/pom.xml
+++ b/distribution/src/main/release/samples/jax_rs/description_swagger2/pom.xml
@@ -60,50 +60,14 @@
</profile>
</profiles>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-dependency-plugin</artifactId>
- <version>2.9</version>
- <executions>
- <execution>
- <phase>generate-resources</phase>
- <goals>
- <goal>unpack</goal>
- </goals>
- <configuration>
- <artifactItems>
- <artifactItem>
- <groupId>org.webjars</groupId>
- <artifactId>swagger-ui</artifactId>
- <version>2.1.0</version>
- <overWrite>true</overWrite>
- <outputDirectory>${project.build.directory}/classes</outputDirectory>
- </artifactItem>
- </artifactItems>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>2.1.0</version>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-http</artifactId>
- <version>3.2.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.2.0-SNAPSHOT</version>
</dependency>
@@ -118,14 +82,6 @@
<version>3.2.0-SNAPSHOT</version>
</dependency>
<dependency>
- <groupId>commons-httpclient</groupId>
- <artifactId>commons-httpclient</artifactId>
- </dependency>
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>javax.ws.rs-api</artifactId>
- </dependency>
- <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
diff --git a/distribution/src/main/release/samples/jax_rs/description_swagger2/src/main/java/demo/jaxrs/swagger/server/Server.java b/distribution/src/main/release/samples/jax_rs/description_swagger2/src/main/java/demo/jaxrs/swagger/server/Server.java
index 9aff7fa..57b459e 100644
--- a/distribution/src/main/release/samples/jax_rs/description_swagger2/src/main/java/demo/jaxrs/swagger/server/Server.java
+++ b/distribution/src/main/release/samples/jax_rs/description_swagger2/src/main/java/demo/jaxrs/swagger/server/Server.java
@@ -25,7 +25,6 @@
import org.apache.cxf.jaxrs.provider.MultipartProvider;
import org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet;
import org.apache.cxf.jaxrs.swagger.Swagger2Feature;
-import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@@ -34,21 +33,10 @@
protected Server() throws Exception {
org.eclipse.jetty.server.Server server = new org.eclipse.jetty.server.Server(9000);
- // Configuring all static web resource
- final ServletHolder staticHolder = new ServletHolder(new DefaultServlet());
- // Register and map the dispatcher servlet
final ServletHolder servletHolder = new ServletHolder(new CXFNonSpringJaxrsServlet());
final ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
- context.addServlet(staticHolder, "/static/*");
context.addServlet(servletHolder, "/*");
- context.setResourceBase(
- getClass().getResource("/META-INF/resources/webjars/swagger-ui/2.1.0").toURI().toString());
-
- servletHolder.setInitParameter("redirects-list",
- "/ /index.html /.*[.]js /css/.* /images/.* lib/.* .*ico /fonts/.*");
- servletHolder.setInitParameter("redirect-servlet-name", staticHolder.getName());
- servletHolder.setInitParameter("redirect-attributes", "javax.servlet.include.request_uri");
servletHolder.setInitParameter("jaxrs.serviceClasses", Sample.class.getName());
servletHolder.setInitParameter("jaxrs.features", Swagger2Feature.class.getName());
servletHolder.setInitParameter("jaxrs.providers", StringUtils.join(
diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java
index c933b30..92ff8b6 100644
--- a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java
+++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java
@@ -18,6 +18,8 @@
*/
package org.apache.cxf.jaxrs.swagger;
+import java.net.URL;
+import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
@@ -31,10 +33,12 @@
public abstract class AbstractSwaggerFeature extends AbstractFeature {
+ protected static final String SWAGGER_UI_RESOURCE_ROOT;
private static final boolean SWAGGER_JAXRS_AVAILABLE;
-
+
static {
SWAGGER_JAXRS_AVAILABLE = isSwaggerJaxRsAvailable();
+ SWAGGER_UI_RESOURCE_ROOT = checkSwaggerUiResourceRoot();
}
protected boolean scan = true;
@@ -60,20 +64,41 @@
return false;
}
}
+ private static String checkSwaggerUiResourceRoot() {
+ try {
+ ClassLoader cl = AbstractSwaggerFeature.class.getClassLoader();
+ if (cl instanceof URLClassLoader) {
+ final String resourcesRootStart = "META-INF/resources/webjars/swagger-ui/";
+ for (URL url : ((URLClassLoader)cl).getURLs()) {
+ String urlStr = url.toString();
+ if (urlStr.contains("/swagger-ui") && urlStr.toString().endsWith(".jar")) {
+ urlStr = urlStr.substring(0, urlStr.length() - 4);
+ String version = urlStr.substring(urlStr.lastIndexOf("/swagger-ui") + 12);
+ return "jar:" + url.toString() + "!/"
+ + resourcesRootStart + version + "/";
+ }
+ }
+
+ }
+ } catch (Throwable ex) {
+ // ignore
+ }
+ return null;
+ }
@Override
public void initialize(Server server, Bus bus) {
if (!activateOnlyIfJaxrsSupported || SWAGGER_JAXRS_AVAILABLE) {
calculateDefaultResourcePackage(server);
calculateDefaultBasePath(server);
- addSwaggerResource(server);
+ addSwaggerResource(server, bus);
initializeProvider(server.getEndpoint(), bus);
- bus.setProperty("swagger.service.descrition.available", "true");
+ bus.setProperty("swagger.service.description.available", "true");
}
}
- protected abstract void addSwaggerResource(Server server);
+ protected abstract void addSwaggerResource(Server server, Bus bus);
protected abstract void setBasePathByAddress(String address);
diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java
index 2722c08..bf6440d 100644
--- a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java
+++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java
@@ -22,12 +22,18 @@
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.regex.Pattern;
import javax.servlet.ServletContext;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
@@ -36,6 +42,7 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import org.apache.cxf.Bus;
import org.apache.cxf.annotations.Provider;
import org.apache.cxf.annotations.Provider.Scope;
import org.apache.cxf.annotations.Provider.Type;
@@ -73,13 +80,22 @@
private boolean scanAllResources;
private String ignoreRoutes;
+
+ private boolean supportSwaggerUi = true;
@Override
- protected void addSwaggerResource(Server server) {
+ protected void addSwaggerResource(Server server, Bus bus) {
+ List<Object> swaggerResources = new LinkedList<Object>();
ApiListingResource apiListingResource = new ApiListingResource();
+ swaggerResources.add(apiListingResource);
+ if (SWAGGER_UI_RESOURCE_ROOT != null && supportSwaggerUi) {
+ swaggerResources.add(new SwaggerUIService());
+ bus.setProperty("swagger.service.ui.available", "true");
+ }
JAXRSServiceFactoryBean sfb =
(JAXRSServiceFactoryBean) server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName());
- sfb.setResourceClassesFromBeans(Collections.<Object>singletonList(apiListingResource));
+ sfb.setResourceClassesFromBeans(swaggerResources);
+
List<ClassResourceInfo> cris = sfb.getClassResourceInfo();
List<Object> providers = new ArrayList<>();
@@ -92,6 +108,9 @@
}
}
}
+ if (SWAGGER_UI_RESOURCE_ROOT != null && supportSwaggerUi) {
+ providers.add(new SwaggerUIFilter());
+ }
providers.add(new Swagger2Serializers(dynamicBasePath, replaceTags, javadocProvider, cris));
providers.add(new ReaderConfigFilter());
((ServerProviderFactory) server.getEndpoint().get(
@@ -100,7 +119,8 @@
BeanConfig beanConfig = new BeanConfig();
beanConfig.setResourcePackage(getResourcePackage());
beanConfig.setVersion(getVersion());
- beanConfig.setBasePath(getBasePath());
+ String basePath = getBasePath();
+ beanConfig.setBasePath(basePath);
beanConfig.setHost(getHost());
beanConfig.setSchemes(getSchemes());
beanConfig.setTitle(getTitle());
@@ -185,7 +205,7 @@
setBasePath(address);
}
}
-
+
@PreMatching
protected static class SwaggerContainerRequestFilter extends ApiListingResource implements ContainerRequestFilter {
@@ -257,4 +277,44 @@
mc.getServletContext().setAttribute(ReaderConfig.class.getName(), rc);
}
}
+ @Path("api-docs")
+ public static class SwaggerUIService {
+ private static final String FAVICON_ICO = "favicon.ico";
+ @GET
+ @Path("{resource:.*}")
+ public Response getResource(@Context UriInfo uriInfo, @PathParam("resource") String resourcePath) {
+ if (FAVICON_ICO.equals(resourcePath)) {
+ return Response.status(404).build();
+ }
+ if (StringUtils.isEmpty(resourcePath) || "/".equals(resourcePath)) {
+ resourcePath = "index.html";
+ }
+ if (resourcePath.startsWith("/")) {
+ resourcePath = resourcePath.substring(1);
+ }
+
+ try {
+ URL resourceURL = URI.create(SWAGGER_UI_RESOURCE_ROOT + resourcePath).toURL();
+ return Response.ok(resourceURL.openStream()).build();
+ } catch (IOException ex) {
+ throw new NotFoundException(ex);
+ }
+ }
+ }
+ @PreMatching
+ protected static class SwaggerUIFilter implements ContainerRequestFilter {
+ private static final Pattern PATTERN =
+ Pattern.compile(".*[.]js|/css/.*|/images/.*|/lib/.*|.*ico|/fonts/.*");
+
+ @Override
+ public void filter(ContainerRequestContext rc) throws IOException {
+ if (HttpMethod.GET.equals(rc.getRequest().getMethod())) {
+ UriInfo ui = rc.getUriInfo();
+ String path = "/" + ui.getPath();
+ if (PATTERN.matcher(path).matches()) {
+ rc.setRequestUri(URI.create("api-docs" + path));
+ }
+ }
+ }
+ }
}
diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java
index 0a4162b..c445209 100644
--- a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java
+++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java
@@ -36,6 +36,7 @@
import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON;
import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider;
+import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
import org.apache.cxf.jaxrs.ext.MessageContext;
@@ -44,7 +45,7 @@
public class SwaggerFeature extends AbstractSwaggerFeature {
@Override
- protected void addSwaggerResource(Server server) {
+ protected void addSwaggerResource(Server server, Bus bus) {
ApiListingResourceJSON apiListingResource = new ApiListingResourceJSON();
if (!runAsFilter) {
List<Object> serviceBeans = new ArrayList<Object>();
diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java
index e94a3ec..ec41cc8 100644
--- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java
+++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java
@@ -199,7 +199,7 @@
public WadlGenerator(Bus bus) {
this.bus = bus;
- this.bus.setProperty("wadl.service.descrition.available", "true");
+ this.bus.setProperty("wadl.service.description.available", "true");
}
@Override
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/servicelist/FormattedServiceListWriter.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/servicelist/FormattedServiceListWriter.java
index ebd20f4..7bb75ae 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/servicelist/FormattedServiceListWriter.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/servicelist/FormattedServiceListWriter.java
@@ -168,12 +168,15 @@
writer.write("<tr><td>");
writer.write("<span class=\"field\">Endpoint address:</span> " + "<span class=\"value\">"
+ absoluteURL + "</span>");
- if (bus != null && PropertyUtils.isTrue(bus.getProperty("wadl.service.descrition.available"))) {
+ if (bus != null && PropertyUtils.isTrue(bus.getProperty("wadl.service.description.available"))) {
writer.write("<br/><span class=\"field\">WADL :</span> " + "<a href=\"" + absoluteURL
+ "?_wadl\">" + absoluteURL + "?_wadl" + "</a>");
}
- if (bus != null && PropertyUtils.isTrue(bus.getProperty("swagger.service.descrition.available"))) {
+ if (bus != null && PropertyUtils.isTrue(bus.getProperty("swagger.service.description.available"))) {
String swaggerPath = "swagger.json";
+ if (PropertyUtils.isTrue(bus.getProperty("swagger.service.ui.available"))) {
+ swaggerPath = "api-docs?url=/" + swaggerPath;
+ }
if (!absoluteURL.endsWith("/")) {
swaggerPath = "/" + swaggerPath;
}