[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;
             }