[CXF-6960] Service SwaggerUI resources if it is available on the classpath, can be disabled
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/f462c797 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/f462c797 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/f462c797 Branch: refs/heads/master-jaxrs-2.1 Commit: f462c797eab75e9b6c6c352b4baeb84429866068 Parents: 22fd16d Author: Sergey Beryozkin <sberyoz...@gmail.com> Authored: Sun Jul 24 21:47:39 2016 +0300 Committer: Sergey Beryozkin <sberyoz...@gmail.com> Committed: Sun Jul 24 21:47:39 2016 +0300 ---------------------------------------------------------------------- .../jax_rs/description_swagger2/README.txt | 9 ++- .../samples/jax_rs/description_swagger2/pom.xml | 44 ------------ .../java/demo/jaxrs/swagger/server/Server.java | 12 ---- .../jaxrs/swagger/AbstractSwaggerFeature.java | 33 +++++++-- .../cxf/jaxrs/swagger/Swagger2Feature.java | 70 ++++++++++++++++++-- .../cxf/jaxrs/swagger/SwaggerFeature.java | 3 +- .../cxf/jaxrs/model/wadl/WadlGenerator.java | 2 +- .../servicelist/FormattedServiceListWriter.java | 7 +- 8 files changed, 108 insertions(+), 72 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/distribution/src/main/release/samples/jax_rs/description_swagger2/README.txt ---------------------------------------------------------------------- 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 @@ are available at 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. + http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/distribution/src/main/release/samples/jax_rs/description_swagger2/pom.xml ---------------------------------------------------------------------- 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,48 +60,12 @@ </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> @@ -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> http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/distribution/src/main/release/samples/jax_rs/description_swagger2/src/main/java/demo/jaxrs/swagger/server/Server.java ---------------------------------------------------------------------- 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.commons.lang3.StringUtils; 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 @@ public class Server { 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( http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java ---------------------------------------------------------------------- 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 @@ import org.apache.cxf.jaxrs.model.ClassResourceInfo; 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 @@ public abstract class AbstractSwaggerFeature extends AbstractFeature { 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); http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java ---------------------------------------------------------------------- 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.io.IOException; 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.MediaType; 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 @@ public class Swagger2Feature extends AbstractSwaggerFeature { 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 @@ public class Swagger2Feature extends AbstractSwaggerFeature { } } } + 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 @@ public class Swagger2Feature extends AbstractSwaggerFeature { 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 @@ public class Swagger2Feature extends AbstractSwaggerFeature { setBasePath(address); } } - + @PreMatching protected static class SwaggerContainerRequestFilter extends ApiListingResource implements ContainerRequestFilter { @@ -257,4 +277,44 @@ public class Swagger2Feature extends AbstractSwaggerFeature { 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)); + } + } + } + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java ---------------------------------------------------------------------- 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.ApiDeclarationProvider; 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 @@ import org.apache.cxf.jaxrs.provider.ServerProviderFactory; 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>(); http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java ---------------------------------------------------------------------- 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 class WadlGenerator implements ContainerRequestFilter { public WadlGenerator(Bus bus) { this.bus = bus; - this.bus.setProperty("wadl.service.descrition.available", "true"); + this.bus.setProperty("wadl.service.description.available", "true"); } @Override http://git-wip-us.apache.org/repos/asf/cxf/blob/f462c797/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/servicelist/FormattedServiceListWriter.java ---------------------------------------------------------------------- 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 @@ public class FormattedServiceListWriter implements ServiceListWriter { 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; }