From 1a253e0ab986e0169c959239cc699af3a79ebc99 Mon Sep 17 00:00:00 2001 From: frantuma Date: Thu, 20 Jul 2017 16:50:34 +0200 Subject: [PATCH] refs swagger-api/swagger-core#2312 - bootstrap draft --- modules/swagger-integration/pom.xml | 74 +++++ .../integration/GenericOpenApiContext.java | 270 +++++++++++++++++ .../integration/GenericOpenApiProcessor.java | 165 ++++++++++ .../integration/GenericOpenApiScanner.java | 91 ++++++ .../oas/integration/OpenApiConfiguration.java | 286 ++++++++++++++++++ .../oas/integration/OpenApiContext.java | 30 ++ .../integration/OpenApiContextLocator.java | 31 ++ .../oas/integration/OpenApiProcessor.java | 19 ++ .../oas/integration/OpenApiReader.java | 11 + .../oas/integration/OpenApiScanner.java | 11 + .../oas/integration/ResourceResolver.java | 43 +++ .../src/main/resources/META-INF/MANIFEST.MF | 27 ++ modules/swagger-jaxrs2/pom.xml | 5 +- .../io/swagger/jaxrs2/OperationParser.java | 4 +- .../main/java/io/swagger/jaxrs2/Reader.java | 18 +- .../AnnotationAndConfigJaxrsScanner.java | 42 +++ .../integration/AnnotationJaxrsScanner.java | 80 +++++ .../jaxrs2/integration/ContextUtils.java | 87 ++++++ .../integration/JaxrsOpenApiContext.java | 37 +++ .../jaxrs2/integration/WebOpenApiContext.java | 14 + .../integration/XmlWebOpenApiContext.java | 75 +++++ .../listing/OpenApiListingResource.java | 87 ++++++ .../listing/BaseApiListingResource.java | 2 +- .../project/resources/ResourceInPackageA.java | 17 ++ .../java/io/swagger/jaxrs2/ReaderTest.java | 30 +- .../annotations/AbstractAnnotationTest.java | 2 +- .../jaxrs2/integration/IntegrationTest.java | 70 +++++ .../project/resources/ResourceInPackageB.java | 17 ++ .../integration/openapi-configuration.json | 20 ++ .../src/main/resources/META-INF/MANIFEST.MF | 21 ++ pom.xml | 3 +- 31 files changed, 1660 insertions(+), 29 deletions(-) create mode 100644 modules/swagger-integration/pom.xml create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiContext.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiProcessor.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiScanner.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiConfiguration.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContext.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContextLocator.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiProcessor.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiReader.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiScanner.java create mode 100644 modules/swagger-integration/src/main/java/io/swagger/oas/integration/ResourceResolver.java create mode 100644 modules/swagger-integration/src/main/resources/META-INF/MANIFEST.MF create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationAndConfigJaxrsScanner.java create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationJaxrsScanner.java create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/ContextUtils.java create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/JaxrsOpenApiContext.java create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/WebOpenApiContext.java create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/XmlWebOpenApiContext.java create mode 100644 modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/listing/OpenApiListingResource.java create mode 100644 modules/swagger-jaxrs2/src/test/java/com/my/project/resources/ResourceInPackageA.java create mode 100644 modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/integration/IntegrationTest.java create mode 100644 modules/swagger-jaxrs2/src/test/java/org/my/project/resources/ResourceInPackageB.java create mode 100644 modules/swagger-jaxrs2/src/test/resources/integration/openapi-configuration.json create mode 100644 modules/swagger-web/src/main/resources/META-INF/MANIFEST.MF diff --git a/modules/swagger-integration/pom.xml b/modules/swagger-integration/pom.xml new file mode 100644 index 0000000000..3eaf1d99d9 --- /dev/null +++ b/modules/swagger-integration/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + io.swagger + swagger-project + 2.0.0-SNAPSHOT + ../.. + + io.swagger + swagger-integration + 2.0.0-SNAPSHOT + bundle + swagger-integration + + + org.reflections + reflections + + + javax.ws.rs + javax.ws.rs-api + 2.0.1 + test + + + org.testng + testng + test + + + io.swagger + swagger-core + ${models.version} + + + + src/main/java + install + + + org.apache.felix + maven-bundle-plugin + ${felix-version} + true + + + set_failok + + manifest + + + + <_failok>true + + + + + + src/main/resources/META-INF + true + + io.swagger.oas.integration + + + + + + + 2.0.0-SNAPSHOT + + diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiContext.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiContext.java new file mode 100644 index 0000000000..256aa3f0a9 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiContext.java @@ -0,0 +1,270 @@ +package io.swagger.oas.integration; + +import io.swagger.oas.models.OpenAPI; +import io.swagger.oas.models.info.Info; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class GenericOpenApiContext implements OpenApiContext { + + private OpenApiConfiguration openApiConfiguration; + + protected String resourcePackage; + + private String basePath = "/"; + + private Map openApiProcessors = new HashMap(); + + protected String id = OPENAPI_CONTEXT_ID_DEFAULT; + protected OpenApiContext parent; + + + public String getResourcePackage() { + return resourcePackage; + } + + public T withResourcePackage(String resourcePackage) { + this.resourcePackage = resourcePackage; + return (T) this; + } + + public String getBasePath() { + return basePath; + } + + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + public final T withBasePath(String basePath) { + this.basePath = basePath; + return (T) this; + } + + public T withOpenApiConfiguration(OpenApiConfiguration openApiConfiguration) { + this.openApiConfiguration = openApiConfiguration; + return (T) this; + } + + public void setOpenApiConfiguration(OpenApiConfiguration openApiConfiguration) { + this.openApiConfiguration = openApiConfiguration; + } + + public String getConfigLocation() { + return configLocation; + } + + public void setConfigLocation(String configLocation) { + this.configLocation = configLocation; + } + + protected String configLocation; + + public final T withConfigLocation(String configLocation) { + this.configLocation = configLocation; + return (T) this; + } + + public void setOpenApiProcessors(Map openApiProcessors) { + this.openApiProcessors = openApiProcessors; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String getId() { + return this.id; + } + + public final T withId(String id) { + this.id = id; + return (T) this; + } + + @Override + public Map getOpenApiProcessors() { + return openApiProcessors; + } + + @Override + public OpenApiProcessor getDefaultProcessor() { + return openApiProcessors.get(id); + } + + + public void setParent(OpenApiContext parent) { + this.parent = parent; + } + + @Override + public OpenApiContext getParent() { + return this.parent; + } + + public final T withParent(OpenApiContext parent) { + this.parent = parent; + return (T) this; + } + + + public GenericOpenApiContext addOpenApiProcessor(OpenApiProcessor openApiProcessor) { + if (StringUtils.isEmpty(openApiProcessor.getId())) { + openApiProcessor.getOpenApiConfiguration().getOpenApi().setInfo( + (openApiProcessor.getOpenApiConfiguration().getOpenApi().getInfo() == null ? + new Info() : + openApiProcessor.getOpenApiConfiguration().getOpenApi().getInfo()).title(id) + ); + } + openApiProcessors.put(openApiProcessor.getId(), openApiProcessor); + return this; + } + + protected OpenApiProcessor buildProcessor(String id, final OpenApiConfiguration openApiConfiguration) throws Exception { + OpenApiProcessor processor; + if (StringUtils.isNotBlank(openApiConfiguration.getProcessorClass())) { + Class cls = getClass().getClassLoader().loadClass(openApiConfiguration.getProcessorClass()); + processor = (OpenApiProcessor) cls.newInstance(); + } else { + processor = new GenericOpenApiProcessor().withId(id).withOpenApiConfiguration(openApiConfiguration); + } + processor.setOpenApiScanner(buildScanner(openApiConfiguration)); + processor.setOpenApiReader(buildReader(openApiConfiguration)); + return processor; + } + + protected OpenApiReader buildReader(final OpenApiConfiguration openApiConfiguration) throws Exception { + OpenApiReader reader; + if (StringUtils.isNotBlank(openApiConfiguration.getReaderClass())) { + Class cls = getClass().getClassLoader().loadClass(openApiConfiguration.getReaderClass()); + // TODO instantiate with configuration + reader = (OpenApiReader) cls.newInstance(); + } else { + reader = new OpenApiReader() { + @Override + public OpenAPI read(Set> classes, Map resources) { + OpenAPI openApi = openApiConfiguration.getOpenApi(); + return openApi; + + } + }; + } + return reader; + } + + protected OpenApiScanner buildScanner(final OpenApiConfiguration openApiConfiguration) throws Exception { + OpenApiScanner scanner; + if (StringUtils.isNotBlank(openApiConfiguration.getScannerClass())) { + Class cls = getClass().getClassLoader().loadClass(openApiConfiguration.getScannerClass()); + // TODO instantiate with configuration + scanner = (OpenApiScanner) cls.newInstance(); + } else { + scanner = new GenericOpenApiScanner(openApiConfiguration); + } + return scanner; + } + + // TODO implement in subclass, also handle classpath + protected URL buildConfigLocationURL(String configLocation) { + + + // TODO CLASSPATH, file path.. + //ServletContext.getResource() + String sanitize = (configLocation.startsWith("/") ? configLocation : "/" + configLocation); + if (true) return this.getClass().getResource(configLocation); + + + + configLocation = "file://" + configLocation; + try { + return new File(configLocation).toURL(); + //return new URL(configLocation); + } catch (MalformedURLException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + protected URL locateConfig() { + if (StringUtils.isNotEmpty(configLocation)) { + return buildConfigLocationURL(configLocation); + } + // TODO check known location in classpath, or same dir or whatever.. + return null; + } + + @Override + public OpenApiContext init() { + + URL configUrl = locateConfig(); + if (configUrl != null) { + // TODO handle urls and stuff, also use loadConfiguration protected now in WebXmlContext.. + Map configurations = OpenApiConfiguration.fromUrl(locateConfig(), id); + for (String id : configurations.keySet()) { + try { + openApiProcessors.put(id, buildProcessor(id, configurations.get(id))); + } catch (Exception e) { + // TODO + e.printStackTrace(); + } + + } + } + + // TODO here try with openApiController? and replace OpenApiConfiguration.fromUrl + + if (openApiProcessors.isEmpty()) { + try { + + if (openApiConfiguration == null) { + openApiConfiguration = new OpenApiConfiguration().withResourcePackage(resourcePackage); + openApiConfiguration.setId(id); + } + + openApiProcessors.put(id, buildProcessor(id, openApiConfiguration)); + } catch (Exception e) { + // TODO + e.printStackTrace(); + } + } + for (OpenApiProcessor p : openApiProcessors.values()) { + p.init(); + } + register(); + return this; + } + + protected void register() { + OpenApiContextLocator.getInstance().putOpenApiContext(id, this); + } + + @Override + public OpenAPI read() { + if (openApiProcessors.isEmpty()) { + return null; + } + return getDefaultProcessor().read(); + } + + @Override + public OpenApiConfiguration getOpenApiConfiguration() { + if (openApiConfiguration != null) { + return openApiConfiguration; + } + if (!openApiProcessors.isEmpty()) { + if (openApiProcessors.get(id) != null) { + return openApiProcessors.get(id).getOpenApiConfiguration(); + } + } + return null; + } + + +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiProcessor.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiProcessor.java new file mode 100644 index 0000000000..f383992f73 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiProcessor.java @@ -0,0 +1,165 @@ +package io.swagger.oas.integration; + + +import com.google.common.base.Objects; +import io.swagger.oas.models.OpenAPI; +import io.swagger.oas.models.info.Info; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class GenericOpenApiProcessor implements OpenApiProcessor { + + protected String id = OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT; + private OpenApiReader openApiReader; + private OpenApiScanner openApiScanner; + + private ConcurrentHashMap cache = new ConcurrentHashMap<>(); + + // 0 doesn't cache + // -1 perpetual + private long cacheTTL = -1; + + public long getCacheTTL() { + return cacheTTL; + } + + public void setCacheTTL(long cacheTTL) { + this.cacheTTL = cacheTTL; + } + + public GenericOpenApiProcessor cacheTTL(long cacheTTL) { + this.cacheTTL = cacheTTL; + return this; + } + + + + public OpenApiReader getOpenApiReader() { + return openApiReader; + } + + public void setOpenApiReader(OpenApiReader openApiReader) { + this.openApiReader = openApiReader; + } + + public OpenApiScanner getOpenApiScanner() { + return openApiScanner; + } + + public void setOpenApiScanner(OpenApiScanner openApiScanner) { + this.openApiScanner = openApiScanner; + } + + @Override + public OpenApiConfiguration getOpenApiConfiguration() { + return openApiConfiguration; + } + + public void setOpenApiConfiguration(OpenApiConfiguration openApiConfiguration) { + this.openApiConfiguration = openApiConfiguration; + } + + private OpenApiConfiguration openApiConfiguration = new OpenApiConfiguration(); + + // TODO used to be basePath, use urls from servers as in comment in configuration + @Override + public String getId() { + return this.id; +/* + if (openApiConfiguration != null) { + if (openApiConfiguration.getOpenApi().getInfo() != null && + !StringUtils.isEmpty(openApiConfiguration.getOpenApi().getInfo().getTitle())){ + return openApiConfiguration.getOpenApi().getInfo().getTitle(); + } + } + return null; +*/ + } + + public void setId (String id) { + /* if (openApiConfiguration != null) { + openApiConfiguration.getOpenApi().setInfo( + (openApiConfiguration.getOpenApi().getInfo() == null ? + new Info() : + openApiConfiguration.getOpenApi().getInfo()).title(id) + ); + }*/ + this.id = id; + } + public GenericOpenApiProcessor withId (String id) { + setId(id); + return this; + } + + public final GenericOpenApiProcessor withOpenApiReader(OpenApiReader openApiReader) { + this.openApiReader = openApiReader; + return this; + } + + public final GenericOpenApiProcessor withOpenApiScanner(OpenApiScanner openApiScanner) { + this.openApiScanner = openApiScanner; + return this; + } + + public final GenericOpenApiProcessor withOpenApiConfiguration(OpenApiConfiguration openApiConfiguration) { + this.openApiConfiguration = openApiConfiguration; + return this; + } + + public final GenericOpenApiProcessor withOpenApiConfigurationFromMap(Map properties) { + this.openApiConfiguration = new OpenApiConfiguration().withProperties(properties); + return this; + } + @Override + public OpenApiProcessor init() { + + if (openApiConfiguration == null) { + openApiConfiguration = new OpenApiConfiguration(); + } + if (openApiScanner == null) { + openApiScanner = new GenericOpenApiScanner(openApiConfiguration); + } + if (openApiReader == null) { + // TODO use a real generic openApi reader only reading openApi annotations.. + openApiReader = new OpenApiReader() { + @Override + public OpenAPI read(Set> classes, Map resources) { + OpenAPI openApi = openApiConfiguration.getOpenApi(); + return openApi; + + } + }; + } + return this; + } + + @Override + public OpenAPI read() { + + if (cacheTTL == 0) { + return getOpenApiReader().read(getOpenApiScanner().classes(), getOpenApiScanner().resources()); + } + Cache cached = cache.get("openapi"); + if (cached == null || cached.isStale(cacheTTL)) { + cached = new Cache(); + cached.createdAt = System.currentTimeMillis(); + cached.openApi = getOpenApiReader().read(getOpenApiScanner().classes(), getOpenApiScanner().resources()); + cache.put("openapi", cached); + } + return cached.openApi; + } + + static class Cache { + long createdAt = 0; + OpenAPI openApi; + + boolean isStale(long cacheTTL) { + return (cacheTTL > 0 && System.currentTimeMillis() - createdAt > cacheTTL); + } + } + + +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiScanner.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiScanner.java new file mode 100644 index 0000000000..2778487cad --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/GenericOpenApiScanner.java @@ -0,0 +1,91 @@ +package io.swagger.oas.integration; + +import org.reflections.Reflections; +import org.reflections.scanners.ResourcesScanner; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class GenericOpenApiScanner implements OpenApiScanner { + + OpenApiConfiguration openApiConfiguration; + + public GenericOpenApiScanner(OpenApiConfiguration openApiConfiguration) { + this.openApiConfiguration = openApiConfiguration; + } + + @Override + public Set> classes() { + ConfigurationBuilder config = new ConfigurationBuilder(); + Set acceptablePackages = new HashSet(); + Set resourceClasses = new HashSet(); + + Set> output = new HashSet>(); + + boolean allowAllPackages = false; + + // if classes are passed, use them + if (openApiConfiguration.getResourceClasses() != null && !"".equals(openApiConfiguration.getResourceClasses())) { + String[] parts = openApiConfiguration.getResourceClasses().split(","); + for (String className : parts) { + if (!"".equals(className)) { + try { + output.add(Class.forName(className)); + } catch (ClassNotFoundException e) { + // TODO + //e.printStackTrace(); + } + } + } + return output; + } + + + if (openApiConfiguration.getResourcePackage() != null && !"".equals(openApiConfiguration.getResourcePackage())) { + String[] parts = openApiConfiguration.getResourcePackage().split(","); + for (String pkg : parts) { + if (!"".equals(pkg)) { + acceptablePackages.add(pkg); + config.addUrls(ClasspathHelper.forPackage(pkg)); + } + } + } else { + allowAllPackages = true; + } + + config.setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(), new SubTypesScanner()); + + final Reflections reflections = new Reflections(config); + // TODO if we add an @Api annotation scan for that, same for defintion + // this is generic, specific Jaxrs scanner will also look for @Path +/* + Set> classes = reflections.getTypesAnnotatedWith(Api.class); + classes.addAll(reflections.getTypesAnnotatedWith(OpenApiDefinition.class)); + + Set> output = new HashSet>(); + for (Class cls : classes) { + if (allowAllPackages) { + output.add(cls); + } else { + for (String pkg : acceptablePackages) { + if (cls.getPackage().getName().startsWith(pkg)) { + output.add(cls); + } + } + } + } +*/ + return output; + } + + @Override + public Map resources() { + return new HashMap(); + } +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiConfiguration.java new file mode 100644 index 0000000000..1f3b2a94b1 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiConfiguration.java @@ -0,0 +1,286 @@ +package io.swagger.oas.integration; + +import com.fasterxml.jackson.core.type.TypeReference; +import io.swagger.oas.models.OpenAPI; +import io.swagger.oas.models.info.Info; +import io.swagger.util.Json; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +public class OpenApiConfiguration { + + private static Logger LOGGER = LoggerFactory.getLogger(OpenApiConfiguration.class); + + private OpenAPI openApi = new OpenAPI(); + + Map rawProperties = new ConcurrentHashMap(); + private boolean basePathAsKey; + + private String id; + private String resourcePackage; + private String filterClass; + private String readerClass; + private String scannerClass; + private String processorClass; + + public String getResourceClasses() { + return resourceClasses; + } + + public void setResourceClasses(String resourceClasses) { + this.resourceClasses = resourceClasses; + } + + private String resourceClasses; + + private boolean pathAsProcessorKey; + + public static Map fromUrl(URL location, String defaultId) { + + // get file as string (for the moment, TODO use commons config) + // load from classpath etc, look from file.. + try { + Map configurationMap = new HashMap<>(); + + String configAsString = readUrl(location); + List configurations = Json.mapper().readValue(configAsString, new TypeReference>() { + }); + for (OpenApiConfiguration config : configurations) { + // TODO we have multiple base paths in 3.0? how to handle? + // TODO use urls as kyes, but need to resolve the url with placeholders and use that? + // for the moment use title instead, +/* + if (config.openApi.getInfo() == null || StringUtils.isEmpty(config.openApi.getInfo().getTitle())){ + if (StringUtils.isEmpty(id)) { + config.openApi.setInfo( + (config.openApi.getInfo() == null ? + new Info() : + config.openApi.getInfo()).title("/") + ); + } else { + config.openApi.setInfo( + (config.openApi.getInfo() == null ? + new Info() : + config.openApi.getInfo()).title(id) + ); + } + } + + configurationMap.put(config.openApi.getInfo().getTitle(), config); +*/ + + configurationMap.put((config.getId() == null) ? defaultId : config.getId(), config); + +/* + if (StringUtils.isEmpty(config.openApi.basePath("/"))){ + if (StringUtils.isEmpty(basePath)) { + config.openApi.basePath("/"); + } else { + config.openApi.basePath(basePath); + } + } + configurationMap.put(config.openApi.getBasePath(), config); +*/ + } + return configurationMap; + + } catch (Exception e) { + // TODO + e.printStackTrace(); + throw new RuntimeException("exception reading config", e); + } + + } + + private static Properties loadProperties(URI uri) throws IOException { + FileReader reader = new FileReader(new File(uri)); + Properties props = new Properties(); + props.load(reader); + reader.close(); + return props; + } + + private static String readUrl(URL url) throws IOException { + StringBuffer sb = new StringBuffer(); + BufferedReader in = new BufferedReader( + new InputStreamReader(url.openStream())); + + String inputLine; + while ((inputLine = in.readLine()) != null) { + sb.append(inputLine).append("\n"); + } + in.close(); + return sb.toString(); + } + + public OpenAPI getOpenApi() { + return openApi; + } + public void setOpenApi (OpenAPI openApi) { + this.openApi = openApi; + } + public OpenApiConfiguration openApi (OpenAPI openApi) { + this.openApi = openApi; + return this; + } + + public boolean isPathAsProcessorKey() { + return pathAsProcessorKey; + } + + public void setPathAsProcessorKey(boolean pathAsProcessorKey) { + this.pathAsProcessorKey = pathAsProcessorKey; + } + + public OpenApiConfiguration withPathAsProcessorKey(boolean pathAsProcessorKey) { + this.pathAsProcessorKey = pathAsProcessorKey; + return this; + } + + public String getReaderClass() { + return readerClass; + } + + public void setReaderClass(String readerClass) { + this.readerClass = readerClass; + } + + public String getScannerClass() { + return scannerClass; + } + + public void setScannerClass(String scannerClass) { + this.scannerClass = scannerClass; + } + + public String getProcessorClass() { + return processorClass; + } + + public void setProcessorClass(String processorClass) { + this.processorClass = processorClass; + } + + public Map getRawProperties() { + return rawProperties; + } + + public void setRawProperties(Map rawProperties) { + this.rawProperties = rawProperties; + } + + public OpenApiConfiguration withScannerClass(String scannerClass) { + this.scannerClass = scannerClass; + return this; + } + + public OpenApiConfiguration withReaderClass(String readerClass) { + this.readerClass = readerClass; + return this; + } + + public OpenApiConfiguration withProcessorClass(String processorClass) { + this.processorClass = processorClass; + return this; + } + + public OpenApiConfiguration withProperties(Map properties) { + if (properties != null) { + rawProperties.putAll(properties); + loadRawProperties(); + } + return this; + } + + // TODO ENUM ETC + private void loadRawProperties() { + for (String key : rawProperties.keySet()) { + switch (key.charAt(0)) { +/* case (RESOURCE_PACKAGE_KEY): + resourcePackage = rawProperties.get(key); + break;*/ + default: + resourcePackage = rawProperties.get(key); + } + } + + } + + public boolean isBasePathAsKey() { + return basePathAsKey; + } + + public void setBasePathAsKey(boolean basePathAsKey) { + this.basePathAsKey = basePathAsKey; + } + + public OpenApiConfiguration withBasePathAsKey(boolean basePathAsKey) { + this.basePathAsKey = basePathAsKey; + return this; + } + + public String getResourcePackage() { + return resourcePackage; + } + + public void setResourcePackage(String resourcePackage) { + this.resourcePackage = resourcePackage; + } + + public OpenApiConfiguration withResourcePackage(String resourcePackage) { + this.resourcePackage = resourcePackage; + return this; + } + + + public String getFilterClass() { + return filterClass; + } + + public void setFilterClass(String filterClass) { + this.filterClass = filterClass; + } + + public OpenApiConfiguration withFilterClass(String filterClass) { + this.filterClass = filterClass; + return this; + } + + + public OpenApiConfiguration withJavaProperties(Properties properties) { + if (properties != null) { + Map map = (Map) properties; + return withProperties(map); + } + return this; + } + + public OpenApiConfiguration withId(String id) { + this.id = id; + return this; + } + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + + public enum CONFIG_FILE_FORMAT {PROPERTIES, INI, JSON, YAML, XML} +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContext.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContext.java new file mode 100644 index 0000000000..04482bb815 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContext.java @@ -0,0 +1,30 @@ +package io.swagger.oas.integration; + + +import io.swagger.oas.models.OpenAPI; + +import java.util.Map; + +public interface OpenApiContext { + + // TODO move to constants + String OPENAPI_CONTEXT_ID_KEY = "openapi.context.id"; + String OPENAPI_CONTEXT_ID_PREFIX = OPENAPI_CONTEXT_ID_KEY + "."; + String OPENAPI_CONTEXT_ID_DEFAULT = OPENAPI_CONTEXT_ID_PREFIX + "default"; + + Map getOpenApiProcessors(); + + String getId(); + + OpenApiContext init(); + + OpenAPI read(); + + OpenApiConfiguration getOpenApiConfiguration(); + + String getConfigLocation(); + + OpenApiProcessor getDefaultProcessor(); + + OpenApiContext getParent(); +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContextLocator.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContextLocator.java new file mode 100644 index 0000000000..fc7c7e3155 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiContextLocator.java @@ -0,0 +1,31 @@ +package io.swagger.oas.integration; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class OpenApiContextLocator { + + // TODO default context? + + private static OpenApiContextLocator instance; + + private ConcurrentMap map = new ConcurrentHashMap(); + + private OpenApiContextLocator() { + } + + public static OpenApiContextLocator getInstance() { + if (instance == null) { + instance = new OpenApiContextLocator(); + } + return instance; + } + + public OpenApiContext getOpenApiContext(String id) { + return map.get(id); + } + + public void putOpenApiContext(String id, OpenApiContext openApiContext) { + map.put(id, openApiContext); + } +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiProcessor.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiProcessor.java new file mode 100644 index 0000000000..70359031f9 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiProcessor.java @@ -0,0 +1,19 @@ +package io.swagger.oas.integration; + +import io.swagger.oas.models.OpenAPI; + +public interface OpenApiProcessor { + + String getId(); + + OpenApiProcessor init(); + + OpenAPI read(); + + void setOpenApiScanner(OpenApiScanner openApiScanner); + + void setOpenApiReader(OpenApiReader openApiReader); + + OpenApiConfiguration getOpenApiConfiguration(); + +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiReader.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiReader.java new file mode 100644 index 0000000000..182810da69 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiReader.java @@ -0,0 +1,11 @@ +package io.swagger.oas.integration; + +import io.swagger.oas.models.OpenAPI; + +import java.util.Map; +import java.util.Set; + +public interface OpenApiReader { + + OpenAPI read(Set> classes, Map resources); +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiScanner.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiScanner.java new file mode 100644 index 0000000000..cdbd915d40 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/OpenApiScanner.java @@ -0,0 +1,11 @@ +package io.swagger.oas.integration; + +import java.util.Map; +import java.util.Set; + +public interface OpenApiScanner { + + Set> classes(); + + Map resources(); +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/oas/integration/ResourceResolver.java b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/ResourceResolver.java new file mode 100644 index 0000000000..95c52cd0c2 --- /dev/null +++ b/modules/swagger-integration/src/main/java/io/swagger/oas/integration/ResourceResolver.java @@ -0,0 +1,43 @@ +package io.swagger.oas.integration; + +import java.io.IOException; + +public class ResourceResolver { + + + public static String readConfiguration(String location) throws IOException { + if (location == null) { + throw new IOException("location null"); + } + // TODO load classpath, file, in jar... consider classloading in osgi/tomcat env + + /* + if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { + // a class path resource (multiple resources for same name possible) + if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { + // a class path resource pattern + return findPathMatchingResources(locationPattern); + } + else { + // all class path resources with the given name + return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); + } + } + else { + // Generally only look for a pattern after a prefix here, + // and on Tomcat only after the "* /" separator for its "war:" protocol. + int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("* /") + 1 : + locationPattern.indexOf(":") + 1); + if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { + // a file pattern + return findPathMatchingResources(locationPattern); + } + else { + // a single resource with the given name + return new Resource[] {getResourceLoader().getResource(locationPattern)}; + } + } + */ + return null; + } +} diff --git a/modules/swagger-integration/src/main/resources/META-INF/MANIFEST.MF b/modules/swagger-integration/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..61b6eb3c54 --- /dev/null +++ b/modules/swagger-integration/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,27 @@ +Manifest-Version: 1.0 +Bnd-LastModified: 1500713605087 +Build-Jdk: 1.8.0_72 +Built-By: frantuma +Bundle-Description: Sonatype helps open source projects to set up Maven + repositories on https://oss.sonatype.org/ +Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.html +Bundle-ManifestVersion: 2 +Bundle-Name: swagger-integration +Bundle-SymbolicName: io.swagger.integration +Bundle-Version: 2.0.0.SNAPSHOT +Created-By: Apache Maven Bundle Plugin +Export-Package: io.swagger.oas.integration;version="2.0.0.SNAPSHOT";uses + :="io.swagger.oas.models" +implementation-version: 2.0.0-SNAPSHOT +Import-Package: com.fasterxml.jackson.core.type;version="[2.8,3)",com.fa + sterxml.jackson.databind;version="[2.8,3)",io.swagger.oas.models;versio + n="[2.0,3)",io.swagger.oas.models.info;version="[2.0,3)",io.swagger.uti + l;version="[2.0,3)",org.apache.commons.lang3;version="[3.2,4)",org.refl + ections;version="[0.9,1)",org.reflections.scanners;version="[0.9,1)",or + g.reflections.util;version="[0.9,1)",org.slf4j;version="[1.7,2)" +mode: development +Originally-Created-By: Apache Maven Bundle Plugin +package: io.swagger +Tool: Bnd-2.1.0.20130426-122213 +url: https://github.com/swagger-api/swagger-core/modules/swagger-integra + tion diff --git a/modules/swagger-jaxrs2/pom.xml b/modules/swagger-jaxrs2/pom.xml index 914767a8d7..bd7edf7d84 100644 --- a/modules/swagger-jaxrs2/pom.xml +++ b/modules/swagger-jaxrs2/pom.xml @@ -166,13 +166,14 @@ io.swagger - swagger-core - ${core.version} + swagger-integration + ${integration.version} 2.0.0-SNAPSHOT 2.0.0-SNAPSHOT 2.0.0-SNAPSHOT + 2.0.0-SNAPSHOT \ No newline at end of file diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/OperationParser.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/OperationParser.java index 9ceaf4b462..b7c184856a 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/OperationParser.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/OperationParser.java @@ -1,6 +1,5 @@ package io.swagger.jaxrs2; -import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.converter.ModelConverters; import io.swagger.jaxrs2.util.ReaderUtils; import io.swagger.oas.annotations.enums.Explode; @@ -24,6 +23,7 @@ import io.swagger.oas.models.servers.ServerVariables; import io.swagger.oas.models.tags.Tag; import io.swagger.util.ParameterProcessor; +import io.swagger.util.Json; import org.apache.commons.lang3.StringUtils; import javax.ws.rs.Produces; @@ -422,7 +422,7 @@ public static Optional getMediaType(MediaType mediaType, ExampleObjec } if (StringUtils.isNotBlank(example.value())) { try { - exampleObject.setValue(new ObjectMapper().readTree(example.value())); + exampleObject.setValue(Json.mapper().readTree(example.value())); } catch (IOException e) { exampleObject.setValue(example.value()); } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/Reader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/Reader.java index e9a31714c2..4f8288de92 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/Reader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/Reader.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -11,6 +10,7 @@ import io.swagger.jaxrs2.ext.OpenAPIExtension; import io.swagger.jaxrs2.ext.OpenAPIExtensions; import io.swagger.jaxrs2.util.ReaderUtils; +import io.swagger.oas.integration.OpenApiReader; import io.swagger.oas.models.Components; import io.swagger.oas.models.OpenAPI; import io.swagger.oas.models.Operation; @@ -24,6 +24,7 @@ import io.swagger.oas.models.parameters.RequestBody; import io.swagger.oas.models.security.SecurityScheme; import io.swagger.oas.models.tags.Tag; +import io.swagger.util.Json; import io.swagger.util.ParameterProcessor; import io.swagger.util.PathUtils; import io.swagger.util.ReflectionUtils; @@ -49,7 +50,7 @@ import java.util.Set; import java.util.TreeSet; -public class Reader { +public class Reader implements OpenApiReader { private static final Logger LOGGER = LoggerFactory.getLogger(Reader.class); public static final String DEFAULT_MEDIA_TYPE_VALUE = "*/*"; @@ -63,7 +64,6 @@ public class Reader { javax.ws.rs.Produces classProduces; javax.ws.rs.Produces methodProduces; - private static final String GET_METHOD = "get"; private static final String POST_METHOD = "post"; private static final String PUT_METHOD = "put"; @@ -73,7 +73,7 @@ public class Reader { private static final String HEAD_METHOD = "head"; private static final String OPTIONS_METHOD = "options"; - public Reader(OpenAPI openAPI, ReaderConfig config) { + public Reader(OpenAPI openAPI) { this.openAPI = openAPI; paths = new Paths(); openApiTags = new LinkedHashSet<>(); @@ -123,6 +123,10 @@ public int compare(Class class1, Class class2) { return openAPI; } + public OpenAPI read(Set> classes, Map resources) { + return read(classes); + } + public OpenAPI read(Class cls, String parentPath) { io.swagger.oas.annotations.security.SecurityScheme apiSecurityScheme = ReflectionUtils.getAnnotation(cls, io.swagger.oas.annotations.security.SecurityScheme.class); io.swagger.oas.annotations.ExternalDocumentation apiExternalDocs = ReflectionUtils.getAnnotation(cls, io.swagger.oas.annotations.ExternalDocumentation.class); @@ -146,7 +150,7 @@ public OpenAPI read(Class cls, String parentPath) { final javax.ws.rs.Path apiPath = ReflectionUtils.getAnnotation(cls, javax.ws.rs.Path.class); JavaType classType = TypeFactory.defaultInstance().constructType(cls); - BeanDescription bd = new ObjectMapper().getSerializationConfig().introspect(classType); + BeanDescription bd = Json.mapper().getSerializationConfig().introspect(classType); final List globalParameters = new ArrayList<>(); @@ -216,7 +220,7 @@ public OpenAPI read(Class cls, String parentPath) { requestBody.setDescription(parameter.getDescription()); isRequestBodyEmpty = false; } - if (parameter.getRequired()) { + if (Boolean.TRUE.equals(parameter.getRequired())) { requestBody.setRequired(parameter.getRequired()); isRequestBodyEmpty = false; } @@ -507,4 +511,4 @@ private String extractOperationIdFromPathItem(PathItem path) { } return ""; } -} \ No newline at end of file +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationAndConfigJaxrsScanner.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationAndConfigJaxrsScanner.java new file mode 100644 index 0000000000..d4d00e7ff8 --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationAndConfigJaxrsScanner.java @@ -0,0 +1,42 @@ +package io.swagger.jaxrs2.integration; + +import javax.servlet.ServletConfig; +import javax.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; + +public class AnnotationAndConfigJaxrsScanner extends AnnotationJaxrsScanner{ + + private Application app; + private ServletConfig sc; + + public AnnotationAndConfigJaxrsScanner withApplication (Application app) { + this.app = app; + return this; + } + + public AnnotationAndConfigJaxrsScanner withServletConfig (ServletConfig s1c) { + this.sc = sc; + return this; + } + @Override + public Set> classes() { + Set> classes = super.classes(); + Set> output = new HashSet>(); + if (app != null) { + Set> clz = app.getClasses(); + if (clz != null) { + output.addAll(clz); + } + Set singletons = app.getSingletons(); + if (singletons != null) { + for (Object o : singletons) { + output.add(o.getClass()); + } + } + } + classes.addAll(output); + return classes; + } + +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationJaxrsScanner.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationJaxrsScanner.java new file mode 100644 index 0000000000..a7ce198c97 --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/AnnotationJaxrsScanner.java @@ -0,0 +1,80 @@ +package io.swagger.jaxrs2.integration; + +import io.swagger.oas.integration.OpenApiConfiguration; +import io.swagger.oas.integration.OpenApiScanner; +import org.reflections.Reflections; +import org.reflections.scanners.ResourcesScanner; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class AnnotationJaxrsScanner> implements OpenApiScanner { + + private OpenApiConfiguration openApiConfiguration; + protected static Logger LOGGER = LoggerFactory.getLogger(AnnotationJaxrsScanner.class); + + public T withOpenApiConfiguration (OpenApiConfiguration openApiConfiguration) { + this.openApiConfiguration = openApiConfiguration; + return (T)this; + } + + @Override + public Set> classes() { + + LOGGER.trace ("classes() - {}", "start"); + if (openApiConfiguration == null) { + LOGGER.trace ("classes() - {}", "config null"); + openApiConfiguration = new OpenApiConfiguration(); + } + + ConfigurationBuilder config = new ConfigurationBuilder(); + Set acceptablePackages = new HashSet(); + + boolean allowAllPackages = false; + if ( openApiConfiguration.getResourcePackage() != null && !"".equals(openApiConfiguration.getResourcePackage())) { + String[] parts = openApiConfiguration.getResourcePackage().split(","); + for (String pkg : parts) { + if (!"".equals(pkg)) { + acceptablePackages.add(pkg); + config.addUrls(ClasspathHelper.forPackage(pkg)); + } + } + } else { + allowAllPackages = true; + } + config.setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(), new SubTypesScanner()); + final Reflections reflections; + reflections = new Reflections(config); + Set> classes = reflections.getTypesAnnotatedWith(javax.ws.rs.Path.class); + // TODO add if adding annotations + //classes.addAll(reflections.getTypesAnnotatedWith(OpenApiDefinition.class)); + + Set> output = new HashSet>(); + for (Class cls : classes) { + if (allowAllPackages) { + output.add(cls); + } else { + for (String pkg : acceptablePackages) { + if (cls.getPackage().getName().startsWith(pkg)) { + output.add(cls); + } + } + } + } + LOGGER.trace ("classes() - output size {}", output.size()); + return output; + } + + @Override + public Map resources() { + return new HashMap(); + } +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/ContextUtils.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/ContextUtils.java new file mode 100644 index 0000000000..481cc1a58a --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/ContextUtils.java @@ -0,0 +1,87 @@ +package io.swagger.jaxrs2.integration; + +import io.swagger.oas.integration.OpenApiConfiguration; +import io.swagger.oas.integration.OpenApiContext; +import io.swagger.oas.integration.OpenApiContextLocator; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.ServletConfig; +import javax.ws.rs.core.Application; + +public class ContextUtils { + + public static final String OPENAPI_CONFIGURATION_RESOURCEPACKAGE_KEY = "openApi.configuration.resourcePackage"; + public static final String OPENAPI_CONFIGURATION_BASEPATH_KEY = "openApi.configuration.basePath"; + public static final String OPENAPI_CONFIGURATION_LOCATION_KEY = "openApi.configuration.location"; + public static final String JERSEY1_PACKAGE_KEY = "com.sun.jersey.config.property.packages"; + public static final String JERSEY2_PACKAGE_KEY = "jersey.config.server.provider.packages"; + + + public static String resolveResourcePackage (ServletConfig servletConfig) { + String resourcePackage = getInitParam (servletConfig, OPENAPI_CONFIGURATION_RESOURCEPACKAGE_KEY); + if (resourcePackage == null) { + // jersey 1 + resourcePackage = getInitParam (servletConfig, JERSEY1_PACKAGE_KEY); + if (resourcePackage != null) { + resourcePackage = resourcePackage.replace(';', ','); + } + } + if (resourcePackage == null) { + // jersey 2 + resourcePackage = getInitParam (servletConfig, JERSEY2_PACKAGE_KEY); + if (resourcePackage != null) { + resourcePackage = resourcePackage.replace(';', ','); + } + } + return resourcePackage; + } + + public static String getInitParam(ServletConfig sc, String paramKey) { + return sc.getInitParameter(paramKey) == null? + sc.getInitParameter(paramKey) : + sc.getInitParameter(paramKey); + } + + public static OpenApiContext getOrBuildContext(OpenApiConfiguration openApiConfiguration) { + return getOrBuildContext(null, null, null, null, null, openApiConfiguration); + } + + public static OpenApiContext getOrBuildContext(String ctxId, Application app, ServletConfig sc, String configLocation, String resourcePackage, OpenApiConfiguration openApiConfiguration) { + // TODO USE instead servletContext attribute? or possibly both + + if (StringUtils.isBlank(ctxId)) { + ctxId = OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT; + } + + OpenApiContext ctx = OpenApiContextLocator.getInstance().getOpenApiContext(ctxId); + + if (ctx == null) { + OpenApiContext rootCtx = OpenApiContextLocator.getInstance().getOpenApiContext(OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT); + ctx = new XmlWebOpenApiContext() + .withServletConfig(sc) + .withApp(app) + .withOpenApiConfiguration(openApiConfiguration) + .withParent(rootCtx); + if (ctx.getConfigLocation() == null && configLocation != null) { + ((XmlWebOpenApiContext)ctx).withConfigLocation(configLocation); + } +/* + if (basePath != null) { + ((XmlWebOpenApiContext)ctx).withBasePath(basePath); + } +*/ + if (resourcePackage != null) { + ((XmlWebOpenApiContext)ctx).withResourcePackage(resourcePackage); + } + ctx.init(); // includes registering itself with OpenApiContextLocator +/* + } else { + OpenApiContextLocator.getInstance().putOpenApiContext(ctxId, ctx); + } +*/ + } + return ctx; + } + + +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/JaxrsOpenApiContext.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/JaxrsOpenApiContext.java new file mode 100644 index 0000000000..92ae9b447e --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/JaxrsOpenApiContext.java @@ -0,0 +1,37 @@ +package io.swagger.jaxrs2.integration; + +import io.swagger.jaxrs2.Reader; +import io.swagger.oas.integration.GenericOpenApiContext; +import io.swagger.oas.integration.OpenApiConfiguration; +import io.swagger.oas.integration.OpenApiContext; +import io.swagger.oas.integration.OpenApiReader; +import io.swagger.oas.integration.OpenApiScanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.core.Application; + +public class JaxrsOpenApiContext extends GenericOpenApiContext implements OpenApiContext { + Logger LOGGER = LoggerFactory.getLogger(JaxrsOpenApiContext.class); + + private Application app; + + public T withApp(Application app) { + this.app = app; + return (T)this; + } + + + @Override + protected OpenApiReader buildReader(OpenApiConfiguration openApiConfiguration) throws Exception { + LOGGER.trace("buildReader"); + return new Reader(openApiConfiguration.getOpenApi()); + } + + @Override + protected OpenApiScanner buildScanner(OpenApiConfiguration openApiConfiguration) throws Exception { + LOGGER.trace("buildscanner"); + return new AnnotationJaxrsScanner().withOpenApiConfiguration(openApiConfiguration); + } + +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/WebOpenApiContext.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/WebOpenApiContext.java new file mode 100644 index 0000000000..9d416c3438 --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/WebOpenApiContext.java @@ -0,0 +1,14 @@ +package io.swagger.jaxrs2.integration; + +import io.swagger.oas.integration.OpenApiContext; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +public interface WebOpenApiContext extends OpenApiContext { + + ServletContext getServletContext(); + ServletConfig getServletConfig(); + + +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/XmlWebOpenApiContext.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/XmlWebOpenApiContext.java new file mode 100644 index 0000000000..3222d6bea1 --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/XmlWebOpenApiContext.java @@ -0,0 +1,75 @@ +package io.swagger.jaxrs2.integration; + +import io.swagger.oas.integration.OpenApiConfiguration; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +import java.net.URL; + +import static io.swagger.jaxrs2.integration.ContextUtils.*; + +public class XmlWebOpenApiContext> extends JaxrsOpenApiContext implements WebOpenApiContext { + + private ServletContext servletContext; + private ServletConfig servletConfig; + + Logger LOGGER = LoggerFactory.getLogger(XmlWebOpenApiContext.class); + + @Override + public ServletContext getServletContext() { + return servletContext; + } + + @Override + public ServletConfig getServletConfig() { + return servletConfig; + } + + public T withServletConfig(ServletConfig servletConfig) { + + if (servletConfig == null) return (T)this; + this.servletConfig = servletConfig; + this.servletContext = servletConfig.getServletContext(); + withId(OPENAPI_CONTEXT_ID_PREFIX + "servlet." + servletConfig.getServletName()); + String location = getInitParam (servletConfig, OPENAPI_CONFIGURATION_LOCATION_KEY); + if (location != null) { + withConfigLocation(location); + } + String resourcePackage = resolveResourcePackage(servletConfig); + if (resourcePackage != null) { + withResourcePackage(resourcePackage); + } + String basePath = getInitParam (servletConfig, OPENAPI_CONFIGURATION_BASEPATH_KEY); + if (basePath != null) { + withBasePath(basePath); + } + return (T)this; + } + + // TODO DRAFT + @Override + protected URL locateConfig() { + + if (StringUtils.isNotEmpty(configLocation)) { + return super.locateConfig(); + } + // check known locations + // /WEB-INF/openApi/openApiconfig.properties + // /WEB-INF/openApi/openApiconfig.json + // /WEB-INF/openApi/openApiconfig.yaml + // /WEB-INF/openApi/openApiconfig... + + // + + // super locate at the end + return super.locateConfig(); + + //return OpenApiConfiguration.fromUri(location, "props"); + + } + +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/listing/OpenApiListingResource.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/listing/OpenApiListingResource.java new file mode 100644 index 0000000000..71e2b0c93e --- /dev/null +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/integration/listing/OpenApiListingResource.java @@ -0,0 +1,87 @@ +package io.swagger.jaxrs2.integration.listing; + +import io.swagger.oas.integration.OpenApiContext; +import io.swagger.oas.models.OpenAPI; +import io.swagger.util.Json; +import io.swagger.util.Yaml; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.ServletConfig; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import static io.swagger.jaxrs2.integration.ContextUtils.*; + +@Path("/openApi.{type:json|yaml}") +public class OpenApiListingResource { + @Context + ServletConfig config; + + @Context + Application app; + + @GET + @Produces({MediaType.APPLICATION_JSON, "application/yaml"}) + public Response getOpenApi(@PathParam("type") String type) throws Exception { + + String ctxId = OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT; + if (config != null) { + ctxId = getInitParam(config, OpenApiContext.OPENAPI_CONTEXT_ID_KEY); + if (StringUtils.isBlank(ctxId)) { + ctxId = OpenApiContext.OPENAPI_CONTEXT_ID_PREFIX + "servlet." + config.getServletName(); + } + } + OpenApiContext ctx = getOrBuildContext(ctxId, app, config, configLocation, resourcePackage, null); + OpenAPI oas = ctx.read(); + + String aa = Json.mapper().writeValueAsString(oas); + System.out.println(aa); + //OpenAPI oas = (OpenAPI)config.getServletContext().getAttribute("oas"); + if (StringUtils.isNotBlank(type) && type.trim().equalsIgnoreCase("yaml")) { + return Response.status(Response.Status.OK) + .entity(Yaml.mapper().writeValueAsString(oas)) + .type("application/yaml") + .build(); + } else { + return Response.status(Response.Status.OK) + .entity(Json.mapper().writeValueAsString(oas)) + .type(MediaType.APPLICATION_JSON_TYPE) + .build(); + } + } + + private String configLocation; + + public String getConfigLocation() { + return configLocation; + } + public void setConfigLocation (String configLocation) { + this.configLocation = configLocation; + } + + public OpenApiListingResource configLocation(String configLocation) { + setConfigLocation(configLocation); + return this; + } + + private String resourcePackage; + + public String getResourcePackage() { + return resourcePackage; + } + public void setResourcePackage (String resourcePackage) { + this.resourcePackage = resourcePackage; + } + + public OpenApiListingResource resourcePackage(String resourcePackage) { + setResourcePackage(resourcePackage); + return this; + } + +} diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/listing/BaseApiListingResource.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/listing/BaseApiListingResource.java index 0056ae5a68..b6913d7b2a 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/listing/BaseApiListingResource.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/jaxrs2/listing/BaseApiListingResource.java @@ -67,7 +67,7 @@ private static synchronized OpenAPI scan(Application app, ServletContext context classes = scanner.classes(); } if (classes != null) { - Reader reader = new Reader(openAPI, ReaderConfigUtils.getReaderConfig(context)); + Reader reader = new Reader(openAPI); // TODO , ReaderConfigUtils.getReaderConfig(context)); openAPI = reader.read(classes); if (scanner instanceof SwaggerConfig) { openAPI = ((SwaggerConfig) scanner).configure(openAPI); diff --git a/modules/swagger-jaxrs2/src/test/java/com/my/project/resources/ResourceInPackageA.java b/modules/swagger-jaxrs2/src/test/java/com/my/project/resources/ResourceInPackageA.java new file mode 100644 index 0000000000..c852bf286d --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/com/my/project/resources/ResourceInPackageA.java @@ -0,0 +1,17 @@ +package com.my.project.resources; + +import io.swagger.oas.annotations.Operation; +import io.swagger.oas.annotations.Parameter; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.util.ArrayList; + +@Path("/packageA") +public class ResourceInPackageA { + @Operation(operationId = "test.") + @GET + public void getTest(@Parameter(name = "test") ArrayList tenantId) { + return; + } +} \ No newline at end of file diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/ReaderTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/ReaderTest.java index f7d322f2de..38ebcf7528 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/ReaderTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/ReaderTest.java @@ -89,7 +89,7 @@ public class ReaderTest { @Test(description = "test a simple resource class") public void testSimpleReadClass() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); OpenAPI openAPI = reader.read(BasicFieldsResource.class); Paths paths = openAPI.getPaths(); assertEquals(PATHS_NUMBER, paths.size()); @@ -104,7 +104,7 @@ public void testSimpleReadClass() { @Test(description = "scan methods") public void testCompleteReadClass() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); OpenAPI openAPI = reader.read(CompleteFieldsResource.class); Paths paths = openAPI.getPaths(); assertEquals(PATHS_NUMBER, paths.size()); @@ -131,7 +131,7 @@ public void testCompleteReadClass() { @Test(description = "scan methods") public void testScanMethods() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = SimpleMethods.class.getMethods(); for (final Method method : methods) { if (isValidRestPath(method)) { @@ -143,7 +143,7 @@ public void testScanMethods() { @Test(description = "Get a Summary and Description") public void testGetSummaryAndDescription() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = BasicFieldsResource.class.getMethods(); Operation operation = reader.parseMethod(methods[0]); assertNotNull(operation); @@ -153,7 +153,7 @@ public void testGetSummaryAndDescription() { @Test(description = "Do nothing because there aren't Operation Annotations") public void testBasicEmptyOperation() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = BasicClass.class.getMethods(); Operation operation = reader.parseMethod(methods[0]); assertNull(operation); @@ -161,7 +161,7 @@ public void testBasicEmptyOperation() { @Test(description = "Get a Duplicated Operation Id") public void testResolveDuplicatedOperationId() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); OpenAPI openAPI = reader.read(DuplicatedOperationIdResource.class); Paths paths = openAPI.getPaths(); @@ -179,7 +179,7 @@ public void testSetOfClasses() { classes.add(SecurityResource.class); classes.add(DuplicatedSecurityResource.class); - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); OpenAPI openAPI = reader.read(classes); assertNotNull(openAPI); Components components = openAPI.getComponents(); @@ -191,7 +191,7 @@ public void testSetOfClasses() { @Test(description = "Deprecated Method") public void testDeprecatedMethod() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = DeprecatedFieldsResource.class.getMethods(); Operation deprecatedOperation = reader.parseMethod(methods[0]); assertNotNull(deprecatedOperation); @@ -200,7 +200,7 @@ public void testDeprecatedMethod() { @Test(description = "Get tags") public void testGetTags() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = TagsResource.class.getMethods(); Operation operation = reader.parseMethod(methods[0]); assertNotNull(operation); @@ -211,7 +211,7 @@ public void testGetTags() { @Test(description = "Responses") public void testGetResponses() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = ResponsesResource.class.getMethods(); @@ -228,7 +228,7 @@ public void testGetResponses() { @Test(description = "Request Body") public void testGetRequestBody() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = RequestBodyResource.class.getMethods(); @@ -241,7 +241,7 @@ public void testGetRequestBody() { @Test(description = "External Docs") public void testGetExternalDocs() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = ExternalDocsReference.class.getMethods(); Operation externalDocsOperation = reader.parseMethod(methods[0]); @@ -253,7 +253,7 @@ public void testGetExternalDocs() { @Test(description = "Security Requirement") public void testSecurityRequirement() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = SecurityResource.class.getMethods(); Operation securityOperation = reader.parseMethod(methods[0]); @@ -270,7 +270,7 @@ public void testSecurityRequirement() { @Test(description = "Callbacks") public void testGetCallbacks() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); Method[] methods = SimpleCallbackResource.class.getMethods(); Operation callbackOperation = reader.parseMethod(methods[0]); assertNotNull(callbackOperation); @@ -295,7 +295,7 @@ public void testGetCallbacks() { @Test(description = "Get the Param of an operation") public void testSubscriptionIdParam() { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); OpenAPI openAPI = reader.read(BasicFieldsResource.class); assertNotNull(openAPI); Paths openAPIPaths = openAPI.getPaths(); diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/annotations/AbstractAnnotationTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/annotations/AbstractAnnotationTest.java index f04d253cc5..b2faaeb5a6 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/annotations/AbstractAnnotationTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/annotations/AbstractAnnotationTest.java @@ -8,7 +8,7 @@ public abstract class AbstractAnnotationTest { public String readIntoYaml(Class cls) { - Reader reader = new Reader(new OpenAPI(), null); + Reader reader = new Reader(new OpenAPI()); OpenAPI openAPI = reader.read(cls); try { diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/integration/IntegrationTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/integration/IntegrationTest.java new file mode 100644 index 0000000000..52d4a78475 --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/jaxrs2/integration/IntegrationTest.java @@ -0,0 +1,70 @@ +package io.swagger.jaxrs2.integration; + +import io.swagger.jaxrs2.Reader; +import io.swagger.oas.integration.GenericOpenApiContext; +import io.swagger.oas.integration.GenericOpenApiProcessor; +import io.swagger.oas.integration.OpenApiConfiguration; +import io.swagger.oas.integration.OpenApiContext; +import io.swagger.oas.integration.OpenApiProcessor; +import io.swagger.oas.models.OpenAPI; +import org.apache.commons.io.FileUtils; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertEquals; + +public class IntegrationTest { + + private final Set expectedKeys = new HashSet(Arrays.asList("/packageA", "/packageB")); + + @Test(description = "scan a simple resource") + public void shouldScanWithNewInitialization() { + OpenApiConfiguration config = new OpenApiConfiguration() + .withResourcePackage("com.my.project.resources,org.my.project.resources") + .openApi(new OpenAPI()); + OpenApiProcessor p = new GenericOpenApiProcessor().withOpenApiConfiguration(config); + + + p.setOpenApiReader(new Reader(config.getOpenApi())); + p.setOpenApiScanner(new AnnotationJaxrsScanner().withOpenApiConfiguration(config)); + OpenApiContext ctx = new GenericOpenApiContext().addOpenApiProcessor(p).init(); + // TODO basePath/url handling + // TODO add getDefaultProcessor + OpenAPI openApi = ctx.getDefaultProcessor().read(); + //OpenAPI openApi = ctx.getOpenApiProcessors().get("/").read(); + + assertNotNull(openApi); + assertEquals(openApi.getPaths().keySet(), expectedKeys); + + + try { + URL url = this.getClass().getResource("/integration/openapi-configuration.json"); + System.out.println(url.getPath()); + //if (true) return; + + //ctx = new XmlWebOpenApiContext().withOpenApiConfiguration(config).init(); + ctx = new XmlWebOpenApiContext().withConfigLocation(url.getPath()).init(); + openApi = ctx.read(); + + assertNotNull(openApi); + assertEquals(openApi.getPaths().keySet(), expectedKeys); + + } catch (Exception e) { + e.printStackTrace(); + } + + //assertEquals(openApi.getSchemes(), expectedSchemas); + } + +} diff --git a/modules/swagger-jaxrs2/src/test/java/org/my/project/resources/ResourceInPackageB.java b/modules/swagger-jaxrs2/src/test/java/org/my/project/resources/ResourceInPackageB.java new file mode 100644 index 0000000000..c6d5cd1a41 --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/org/my/project/resources/ResourceInPackageB.java @@ -0,0 +1,17 @@ +package org.my.project.resources; + +import io.swagger.oas.annotations.Operation; +import io.swagger.oas.annotations.Parameter; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.util.ArrayList; + +@Path("/packageB") +public class ResourceInPackageB { + @Operation(operationId = "test.") + @GET + public void getTest(@Parameter(name = "test") ArrayList tenantId) { + return; + } +} \ No newline at end of file diff --git a/modules/swagger-jaxrs2/src/test/resources/integration/openapi-configuration.json b/modules/swagger-jaxrs2/src/test/resources/integration/openapi-configuration.json new file mode 100644 index 0000000000..63b3aec340 --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/resources/integration/openapi-configuration.json @@ -0,0 +1,20 @@ +[ + { + "resourcePackage": "com.my.project.resources,org.my.project.resources", + "openApi": { + "info": { + "version": "1.0", + "title": "Swagger Pet Sample App", + "description": "This is a TEST AAsample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + } + } + } +] \ No newline at end of file diff --git a/modules/swagger-web/src/main/resources/META-INF/MANIFEST.MF b/modules/swagger-web/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..8a4081298e --- /dev/null +++ b/modules/swagger-web/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bnd-LastModified: 1500656385778 +Build-Jdk: 1.8.0_72 +Built-By: frantuma +Bundle-Description: Sonatype helps open source projects to set up Maven + repositories on https://oss.sonatype.org/ +Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.html +Bundle-ManifestVersion: 2 +Bundle-Name: swagger-web +Bundle-SymbolicName: io.swagger.web +Bundle-Version: 2.0.0.SNAPSHOT +Created-By: Apache Maven Bundle Plugin +Export-Package: io.swagger.oas.web;version="2.0.0.SNAPSHOT";uses:="io.sw + agger.oas.models" +implementation-version: 2.0.0-SNAPSHOT +Import-Package: io.swagger.oas.models;version="[2.0,3)" +mode: development +Originally-Created-By: Apache Maven Bundle Plugin +package: io.swagger +Tool: Bnd-2.1.0.20130426-122213 +url: https://github.com/swagger-api/swagger-core/modules/swagger-web diff --git a/pom.xml b/pom.xml index bd9f38ad90..621d79595a 100644 --- a/pom.xml +++ b/pom.xml @@ -345,9 +345,10 @@ modules/swagger-annotations modules/swagger-models - modules/swagger-jaxrs2 modules/swagger-core modules/swagger-web + modules/swagger-integration + modules/swagger-jaxrs2