blob: bf6440dbf894025722947a491c59c5708e90f067 [file] [log] [blame]
/**
* 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.swagger;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
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;
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 org.apache.cxf.Bus;
import org.apache.cxf.annotations.Provider;
import org.apache.cxf.annotations.Provider.Scope;
import org.apache.cxf.annotations.Provider.Type;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.jaxrs.model.doc.DocumentationProvider;
import org.apache.cxf.jaxrs.model.doc.JavaDocProvider;
import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
import org.apache.cxf.jaxrs.utils.InjectionUtils;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.config.DefaultReaderConfig;
import io.swagger.jaxrs.config.ReaderConfig;
import io.swagger.jaxrs.listing.ApiListingResource;
@Provider(value = Type.Feature, scope = Scope.Server)
public class Swagger2Feature extends AbstractSwaggerFeature {
protected boolean dynamicBasePath;
protected boolean replaceTags;
protected DocumentationProvider javadocProvider;
private String host;
private String[] schemes;
private boolean prettyPrint;
private boolean scanAllResources;
private String ignoreRoutes;
private boolean supportSwaggerUi = true;
@Override
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(swaggerResources);
List<ClassResourceInfo> cris = sfb.getClassResourceInfo();
List<Object> providers = new ArrayList<>();
if (runAsFilter) {
providers.add(new SwaggerContainerRequestFilter());
} else {
for (ClassResourceInfo cri : cris) {
if (ApiListingResource.class == cri.getResourceClass()) {
InjectionUtils.injectContextProxies(cri, apiListingResource);
}
}
}
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(
ServerProviderFactory.class.getName())).setUserProviders(providers);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setResourcePackage(getResourcePackage());
beanConfig.setVersion(getVersion());
String basePath = getBasePath();
beanConfig.setBasePath(basePath);
beanConfig.setHost(getHost());
beanConfig.setSchemes(getSchemes());
beanConfig.setTitle(getTitle());
beanConfig.setDescription(getDescription());
beanConfig.setContact(getContact());
beanConfig.setLicense(getLicense());
beanConfig.setLicenseUrl(getLicenseUrl());
beanConfig.setTermsOfServiceUrl(getTermsOfServiceUrl());
beanConfig.setScan(isScan());
beanConfig.setPrettyPrint(isPrettyPrint());
beanConfig.setFilterClass(getFilterClass());
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String[] getSchemes() {
return schemes;
}
public void setSchemes(String[] schemes) {
this.schemes = schemes;
}
public boolean isPrettyPrint() {
return prettyPrint;
}
public void setPrettyPrint(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
}
public boolean isScanAllResources() {
return scanAllResources;
}
public void setScanAllResources(boolean scanAllResources) {
this.scanAllResources = scanAllResources;
}
public String getIgnoreRoutes() {
return ignoreRoutes;
}
public void setIgnoreRoutes(String ignoreRoutes) {
this.ignoreRoutes = ignoreRoutes;
}
public void setDynamicBasePath(final boolean dynamicBasePath) {
this.dynamicBasePath = dynamicBasePath;
}
public void setReplaceTags(final boolean replaceTags) {
this.replaceTags = replaceTags;
}
public void setJavaDocPath(final String javaDocPath) throws Exception {
this.javadocProvider = new JavaDocProvider(javaDocPath);
}
public void setJavaDocPaths(final String... javaDocPaths) throws Exception {
this.javadocProvider = new JavaDocProvider(javaDocPaths);
}
public void setJavaDocURLs(final URL[] javaDocURLs) {
this.javadocProvider = new JavaDocProvider(javaDocURLs);
}
@Override
protected void setBasePathByAddress(String address) {
if (!address.startsWith("/")) {
// get the path part
URI u = URI.create(address);
setBasePath(u.getPath());
setHost(u.getPort() < 0 ? u.getHost() : u.getHost() + ":" + u.getPort());
} else {
setBasePath(address);
}
}
@PreMatching
protected static class SwaggerContainerRequestFilter extends ApiListingResource implements ContainerRequestFilter {
protected static final MediaType APPLICATION_YAML_TYPE = JAXRSUtils.toMediaType("application/yaml");
protected static final String APIDOCS_LISTING_PATH = "swagger";
protected static final String APIDOCS_LISTING_PATH_JSON = APIDOCS_LISTING_PATH + ".json";
protected static final String APIDOCS_LISTING_PATH_YAML = APIDOCS_LISTING_PATH + ".yaml";
@Context
protected MessageContext mc;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
UriInfo ui = mc.getUriInfo();
List<MediaType> mediaTypes = mc.getHttpHeaders().getAcceptableMediaTypes();
Response response = null;
if ((ui.getPath().endsWith(APIDOCS_LISTING_PATH)
&& !JAXRSUtils.intersectMimeTypes(mediaTypes, MediaType.APPLICATION_JSON_TYPE).isEmpty())
|| ui.getPath().endsWith(APIDOCS_LISTING_PATH_JSON)) {
response = getListingJsonResponse(
null, mc.getServletContext(), mc.getServletConfig(), mc.getHttpHeaders(), ui);
} else if ((ui.getPath().endsWith(APIDOCS_LISTING_PATH)
&& !JAXRSUtils.intersectMimeTypes(mediaTypes, APPLICATION_YAML_TYPE).isEmpty())
|| ui.getPath().endsWith(APIDOCS_LISTING_PATH_YAML)) {
response = getListingYamlResponse(
null, mc.getServletContext(), mc.getServletConfig(), mc.getHttpHeaders(), ui);
}
if (response != null) {
requestContext.abortWith(response);
}
}
}
protected class ReaderConfigFilter implements ContainerRequestFilter {
@Context
protected MessageContext mc;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
ServletContext servletContext = mc.getServletContext();
if (servletContext != null && servletContext.getAttribute(ReaderConfig.class.getName()) == null) {
if (mc.getServletConfig() != null
&& Boolean.valueOf(mc.getServletConfig().getInitParameter("scan.all.resources"))) {
addReaderConfig(mc.getServletConfig().getInitParameter("ignore.routes"));
} else if (isScanAllResources()) {
addReaderConfig(getIgnoreRoutes());
}
}
}
protected void addReaderConfig(String ignoreRoutesParam) {
DefaultReaderConfig rc = new DefaultReaderConfig();
rc.setScanAllResources(true);
if (ignoreRoutesParam != null) {
Set<String> routes = new LinkedHashSet<>();
for (String route : StringUtils.split(ignoreRoutesParam, ",")) {
routes.add(route.trim());
}
rc.setIgnoredRoutes(routes);
}
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));
}
}
}
}
}