servicemix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ff...@apache.org
Subject servicemix-bundles git commit: [SM-2480]swagger-jaxrs should support multiple swagger enabled endpoints in OSGi container
Date Tue, 17 Mar 2015 05:24:27 GMT
Repository: servicemix-bundles
Updated Branches:
  refs/heads/master 4194f2195 -> 85bbf6075


[SM-2480]swagger-jaxrs should support multiple swagger enabled endpoints in OSGi container


Project: http://git-wip-us.apache.org/repos/asf/servicemix-bundles/repo
Commit: http://git-wip-us.apache.org/repos/asf/servicemix-bundles/commit/85bbf607
Tree: http://git-wip-us.apache.org/repos/asf/servicemix-bundles/tree/85bbf607
Diff: http://git-wip-us.apache.org/repos/asf/servicemix-bundles/diff/85bbf607

Branch: refs/heads/master
Commit: 85bbf607527bfaae05e96e7fc5a9b4cfc704aabc
Parents: 4194f21
Author: Freeman Fang <freeman.fang@gmail.com>
Authored: Tue Mar 17 13:24:05 2015 +0800
Committer: Freeman Fang <freeman.fang@gmail.com>
Committed: Tue Mar 17 13:24:05 2015 +0800

----------------------------------------------------------------------
 pom.xml                                         |   7 +
 swagger-jaxrs-1.3.10/pom.xml                    |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 swagger-jaxrs-1.3.11/pom.xml                    |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 swagger-jaxrs-1.3.12/pom.xml                    |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 swagger-jaxrs-1.3.4/pom.xml                     |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 swagger-jaxrs-1.3.5/pom.xml                     |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 swagger-jaxrs-1.3.7/pom.xml                     |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 swagger-jaxrs-1.3.8/pom.xml                     |  27 +++
 .../swagger/jaxrs/listing/ApiListing.scala      | 176 +++++++++++++++++++
 15 files changed, 1428 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3f38395..07a7f40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -108,6 +108,13 @@
         <module>htmlunit-2.15</module>
         <module>opensaml-3.1.0</module>
         <module>swagger-jaxrs-1.3.2</module>
+        <module>swagger-jaxrs-1.3.4</module>
+        <module>swagger-jaxrs-1.3.5</module>
+        <module>swagger-jaxrs-1.3.7</module>
+        <module>swagger-jaxrs-1.3.8</module>
+        <module>swagger-jaxrs-1.3.10</module>
+        <module>swagger-jaxrs-1.3.11</module>
+        <module>swagger-jaxrs-1.3.12</module>
     </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.10/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.10/pom.xml b/swagger-jaxrs-1.3.10/pom.xml
index ad2e4a9..5ab07e6 100644
--- a/swagger-jaxrs-1.3.10/pom.xml
+++ b/swagger-jaxrs-1.3.10/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.10/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.10/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.10/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.10/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.11/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.11/pom.xml b/swagger-jaxrs-1.3.11/pom.xml
index 0cedc6b..28fa809 100644
--- a/swagger-jaxrs-1.3.11/pom.xml
+++ b/swagger-jaxrs-1.3.11/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.11/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.11/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.11/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.11/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.12/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.12/pom.xml b/swagger-jaxrs-1.3.12/pom.xml
index 20aab88..0654650 100644
--- a/swagger-jaxrs-1.3.12/pom.xml
+++ b/swagger-jaxrs-1.3.12/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.12/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.12/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.12/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.12/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.4/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.4/pom.xml b/swagger-jaxrs-1.3.4/pom.xml
index 89fb8b2..3d9340f 100644
--- a/swagger-jaxrs-1.3.4/pom.xml
+++ b/swagger-jaxrs-1.3.4/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.4/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.4/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.4/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.4/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.5/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.5/pom.xml b/swagger-jaxrs-1.3.5/pom.xml
index 02a6c58..5b27b82 100644
--- a/swagger-jaxrs-1.3.5/pom.xml
+++ b/swagger-jaxrs-1.3.5/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.5/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.5/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.5/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.5/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.7/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.7/pom.xml b/swagger-jaxrs-1.3.7/pom.xml
index 9b2b9f8..0ecb6bc 100644
--- a/swagger-jaxrs-1.3.7/pom.xml
+++ b/swagger-jaxrs-1.3.7/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.7/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.7/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.7/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.7/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.8/pom.xml
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.8/pom.xml b/swagger-jaxrs-1.3.8/pom.xml
index f7327dd..66dea7d 100644
--- a/swagger-jaxrs-1.3.8/pom.xml
+++ b/swagger-jaxrs-1.3.8/pom.xml
@@ -63,11 +63,38 @@
             <classifier>sources</classifier>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>scala-compile-first</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                                <goal>compile</goal>
+                            </goals>
+                        </execution>
+                        <execution>
+                            <id>scala-test-compile</id>
+                            <phase>process-test-resources</phase>
+                            <goals>
+                                <goal>testCompile</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+            </plugin>
+
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>

http://git-wip-us.apache.org/repos/asf/servicemix-bundles/blob/85bbf607/swagger-jaxrs-1.3.8/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
----------------------------------------------------------------------
diff --git a/swagger-jaxrs-1.3.8/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala b/swagger-jaxrs-1.3.8/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
new file mode 100644
index 0000000..dd7c6d0
--- /dev/null
+++ b/swagger-jaxrs-1.3.8/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala
@@ -0,0 +1,176 @@
+package com.wordnik.swagger.jaxrs.listing
+
+import com.wordnik.swagger.config._
+import com.wordnik.swagger.reader._
+import com.wordnik.swagger.core.util._
+import com.wordnik.swagger.model._
+import com.wordnik.swagger.core.filter._
+import com.wordnik.swagger.annotations._
+import com.wordnik.swagger.jaxrs._
+import com.wordnik.swagger.jaxrs.config._
+
+import org.slf4j.LoggerFactory
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+import javax.ws.rs.core.{ UriInfo, HttpHeaders, Context, Response, MediaType, Application, MultivaluedMap }
+import javax.ws.rs.core.Response._
+import javax.ws.rs._
+import javax.ws.rs.ext.Provider
+
+import javax.servlet.ServletConfig
+import java.util.HashMap
+
+import scala.collection.mutable.LinkedHashMap
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
+
+object ApiListingCache {
+  private val LOGGER = LoggerFactory.getLogger(ApiListingCache.getClass)
+  
+  var _cache: Option[Map[String, ApiListing]] = None
+  
+  var caches: java.util.Map[Object, Option[Map[String, ApiListing]]] = new HashMap[Object, Option[Map[String, ApiListing]]]
+
+  def listing(docRoot: String, app: Application, sc: ServletConfig): Option[Map[String, ApiListing]] = {
+    val scanner = sc.getServletContext().getAttribute("SCANNER")
+    if (scanner != null) {
+        _cache = caches.get(scanner)
+    }
+    if (_cache == null) {
+        _cache = None
+    }
+    _cache.orElse{
+      LOGGER.debug("loading cache")
+      ClassReaders.reader.map{reader => 
+        
+          val classes = scanner match {
+            case scanner: JaxrsScanner => scanner.asInstanceOf[JaxrsScanner].classesFromContext(app, null)
+            case _ => List()
+          }
+          // For each top level resource, parse it and look for swagger annotations.
+          val listings = (for(cls <- classes) yield reader.read(docRoot, cls, ConfigFactory.config)).flatten.toList
+          _cache = Some((listings.map(m => {
+            // always start with "/"
+            val resourcePath = m.resourcePath.startsWith ("/") match {
+              case true => m.resourcePath
+              case false => "/" + m.resourcePath
+            }
+            LOGGER.debug("adding resource path " + resourcePath)
+            (resourcePath, m)
+          })).toMap)
+        
+        ;
+      }
+      _cache
+    }
+    caches.put(scanner, _cache)
+    if(_cache != None)
+      LOGGER.debug("cache has " + _cache.get.keys + " keys")
+    else
+      LOGGER.debug("cache is empty")
+    _cache
+  }
+
+  def invalidateCache() = {
+    _cache = None
+  }
+}
+
+class ApiListingResource {
+  private val LOGGER = LoggerFactory.getLogger(classOf[ApiListingResource])
+
+  @GET
+  def resourceListing (
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) 
+        yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      ).filter(m => m.apis.size > 0)
+    })
+    val references = (for(listing <- listings.getOrElse(List())) yield {
+      ApiListingReference(listing.resourcePath, listing.description, listing.position)
+    }).toList.sortWith(_.position < _.position)
+
+    val config = ConfigFactory.config
+    val resourceListing = ResourceListing(config.apiVersion,
+      config.swaggerVersion,
+      references,
+      config.authorizations,
+      config.info
+    )
+    Response.ok(resourceListing).build
+  }
+
+  /**
+   * individual api listing
+   **/
+  @GET
+  @Path("/{route: .+}")
+  def apiDeclaration (
+    @PathParam("route") route: String,
+    @Context app: Application,
+    @Context sc: ServletConfig,
+    @Context headers: HttpHeaders,
+    @Context uriInfo: UriInfo
+  ): Response = {
+    LOGGER.debug("requested apiDeclaration for " + route)
+    val docRoot = this.getClass.getAnnotation(classOf[Path]).value
+    val f = new SpecFilter
+    val pathPart = cleanRoute(route)
+    LOGGER.debug("requested route " + pathPart)
+    val listings = ApiListingCache.listing(docRoot, app, sc).map(specs => {
+      (for(spec <- specs.values) yield {
+        LOGGER.debug("inspecting path " + spec.resourcePath)
+        f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
+      }).filter(m => {
+        val resourcePath = m.resourcePath match {
+          case e: String if(e.startsWith("/")) => e
+          case e: String => "/" + e
+        }
+        resourcePath == pathPart
+      })
+    }).toList.flatten
+
+    listings.size match {
+      case 1 => Response.ok(listings(0)).build
+      case _ => Response.status(404).build
+    }
+  }
+
+  // ensure leading slash, remove trailing
+  def cleanRoute(route: String) = {
+    val cleanStart = {
+      if(route.startsWith("/")) route
+      else "/" + route
+    }
+    if(cleanStart.endsWith("/")) cleanStart.substring(0, cleanStart.length - 1)
+    else cleanStart
+  }
+
+  def invalidateCache() = {
+    ApiListingCache.invalidateCache()
+  }
+
+  def paramsToMap(params: MultivaluedMap[String, String]): Map[String, List[String]] = {
+    (for((key, list) <- params.asScala) yield (key, list.asScala.toList)).toMap
+  }
+
+  def cookiesToMap(headers: HttpHeaders): Map[String, String] = {
+    Option(headers).map(h => {
+      (for((name, cookie) <- h.getCookies.asScala) yield (name, cookie.getValue)).toMap
+    }).getOrElse(Map())
+  }
+
+  def headersToMap(headers: HttpHeaders): Map[String, List[String]] = {
+    (for((key, values) <- headers.getRequestHeaders.asScala) yield (key, values.asScala.toList)).toMap
+  }
+}
\ No newline at end of file


Mime
View raw message