You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/11/19 00:42:51 UTC
[07/24] incubator-brooklyn git commit: [BROOKLYN-185] Move
ApidocResource(s) to swagger 2.0 spec standard
[BROOKLYN-185] Move ApidocResource(s) to swagger 2.0 spec standard
Add SwaggerFilter to web.xml, initializing the appropriate context
parameters for new swagger resource scanners.
Use a custom ApiListingResource instead of swagger-provided resource so
we can use swagger within a jersey servlet filter context.
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/5c625626
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/5c625626
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/5c625626
Branch: refs/heads/master
Commit: 5c62562640affb17d23eb87048933aa47b8230bd
Parents: 55c5904
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Tue Nov 10 01:09:54 2015 +0200
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Wed Nov 11 17:42:43 2015 +0200
----------------------------------------------------------------------
.../camp/server/rest/CampRestResources.java | 10 +-
.../rest/resource/ApidocRestResource.java | 2 +-
usage/rest-api/src/main/webapp/WEB-INF/web.xml | 10 +-
.../apache/brooklyn/rest/BrooklynRestApi.java | 2 -
.../brooklyn/rest/filter/SwaggerFilter.java | 94 ++++++
.../brooklyn/rest/resources/ApidocResource.java | 7 +-
.../rest-server/src/main/webapp/WEB-INF/web.xml | 13 +-
.../brooklyn/rest/BrooklynRestApiLauncher.java | 3 +-
.../rest/resources/ApiDocResourceTest.java | 138 ---------
.../rest/resources/ApidocResourceTest.java | 135 +++++++++
.../rest/testing/BrooklynRestApiTest.java | 8 +
.../rest/util/NullServletConfigProvider.java | 5 +
utils/rest-swagger/pom.xml | 16 +-
.../rest/apidoc/ApiListingResource.java | 259 ++++++++++++++++
.../brooklyn/rest/apidoc/ApidocEndpoint.java | 54 ----
.../apidoc/ApidocHelpMessageBodyWriter.java | 28 --
.../brooklyn/rest/apidoc/ApidocResource.java | 294 -------------------
.../apache/brooklyn/rest/apidoc/ApidocRoot.java | 47 ---
.../rest/apidoc/RestApiResourceScanner.java | 79 +++++
utils/swagger-annotations/pom.xml | 44 +++
.../brooklyn/swagger/annotations/Apidoc.java | 33 +++
21 files changed, 697 insertions(+), 584 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/CampRestResources.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/CampRestResources.java b/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/CampRestResources.java
index 2d3030c..0c99377 100644
--- a/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/CampRestResources.java
+++ b/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/CampRestResources.java
@@ -30,15 +30,15 @@ import org.apache.brooklyn.camp.server.rest.resource.AssemblyTemplateRestResourc
import org.apache.brooklyn.camp.server.rest.resource.PlatformComponentRestResource;
import org.apache.brooklyn.camp.server.rest.resource.PlatformComponentTemplateRestResource;
import org.apache.brooklyn.camp.server.rest.resource.PlatformRestResource;
-import org.apache.brooklyn.rest.apidoc.ApidocHelpMessageBodyWriter;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.google.common.collect.Iterables;
+import io.swagger.jaxrs.listing.SwaggerSerializers;
public class CampRestResources {
public static Iterable<AbstractCampRestResource> getCampRestResources() {
- List<AbstractCampRestResource> resources = new ArrayList<AbstractCampRestResource>();
+ List<AbstractCampRestResource> resources = new ArrayList<>();
resources.add(new PlatformRestResource());
resources.add(new AssemblyTemplateRestResource());
resources.add(new PlatformComponentTemplateRestResource());
@@ -50,14 +50,14 @@ public class CampRestResources {
}
public static Iterable<Object> getApidocResources() {
- List<Object> resources = new ArrayList<Object>();
- resources.add(new ApidocHelpMessageBodyWriter());
+ List<Object> resources = new ArrayList<>();
resources.add(new ApidocRestResource());
return resources;
}
public static Iterable<Object> getMiscResources() {
- List<Object> resources = new ArrayList<Object>();
+ List<Object> resources = new ArrayList<>();
+ resources.add(new SwaggerSerializers());
resources.add(new JacksonJsonProvider());
return resources;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/resource/ApidocRestResource.java
----------------------------------------------------------------------
diff --git a/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/resource/ApidocRestResource.java b/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/resource/ApidocRestResource.java
index e410f4c..850ae22 100644
--- a/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/resource/ApidocRestResource.java
+++ b/camp/camp-server/src/main/java/org/apache/brooklyn/camp/server/rest/resource/ApidocRestResource.java
@@ -24,7 +24,7 @@ import javax.ws.rs.Path;
@Path(ApidocRestResource.API_URI_PATH)
@Api(value = "org.apache.brooklyn.camp.server.rest.resource.ApidocRestResource", description = "Web API Documentation")
-public class ApidocRestResource extends org.apache.brooklyn.rest.apidoc.ApidocResource {
+public class ApidocRestResource extends org.apache.brooklyn.rest.apidoc.ApiListingResource {
public static final String API_URI_PATH = PlatformRestResource.CAMP_URI_PATH + "/apidoc";
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-api/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/webapp/WEB-INF/web.xml b/usage/rest-api/src/main/webapp/WEB-INF/web.xml
index 672785e..06331bd 100644
--- a/usage/rest-api/src/main/webapp/WEB-INF/web.xml
+++ b/usage/rest-api/src/main/webapp/WEB-INF/web.xml
@@ -59,6 +59,14 @@
<filter-name>Brooklyn HA Master Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
+ <filter>
+ <filter-name>Brooklyn Swagger Bootstrap</filter-name>
+ <filter-class>org.apache.brooklyn.rest.filter.SwaggerFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>Brooklyn Swagger Bootstrap</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
<!-- Brooklyn REST is usually run as a filter so static content can be placed in a webapp
to which this is added; to run as a servlet directly, replace the filter tags
@@ -75,7 +83,7 @@
<!-- load our REST API jersey resources -->
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
- <param-value>org.apache.brooklyn.rest.resources;org.apache.brooklyn.rest.apidoc</param-value>
+ <param-value>io.swagger.jaxrs.listing;org.codehaus.jackson.jaxrs;org.apache.brooklyn.rest.resources;org.apache.brooklyn.rest.util</param-value>
</init-param>
<!-- install Jackson and turn on pojo/json serialization (could add org.codehaus.jackson.jaxrs
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
index 3973e52..209eb94 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
@@ -21,7 +21,6 @@ package org.apache.brooklyn.rest;
import java.util.ArrayList;
import java.util.List;
-import org.apache.brooklyn.rest.apidoc.ApidocHelpMessageBodyWriter;
import org.apache.brooklyn.rest.resources.AbstractBrooklynRestResource;
import org.apache.brooklyn.rest.resources.AccessResource;
import org.apache.brooklyn.rest.resources.ActivityResource;
@@ -70,7 +69,6 @@ public class BrooklynRestApi {
public static Iterable<Object> getApidocResources() {
List<Object> resources = new ArrayList<Object>();
- resources.add(new ApidocHelpMessageBodyWriter());
resources.add(new ApidocResource());
return resources;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/SwaggerFilter.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/SwaggerFilter.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/SwaggerFilter.java
new file mode 100644
index 0000000..5159b6e
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/SwaggerFilter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.brooklyn.rest.filter;
+
+import io.swagger.config.ScannerFactory;
+import io.swagger.models.Info;
+import io.swagger.models.License;
+import io.swagger.models.Swagger;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import org.apache.brooklyn.rest.apidoc.RestApiResourceScanner;
+
+/**
+ * Bootstraps swagger.
+ *
+ * Swagger was intended to run as a servlet.
+ *
+ * @author Ciprian Ciubotariu <ch...@gmx.net>
+ */
+public class SwaggerFilter implements Filter {
+
+ static Info info = new Info()
+ .title("Brooklyn ApiDoc")
+ .version("TODO") // API version, not BROOKLYN_VERSION
+ // .description("This is a sample 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(new Contact()
+ // .email("apiteam@swagger.io"))
+ .license(new License()
+ .name("Apache 2.0")
+ .url("http://www.apache.org/licenses/LICENSE-2.0.html"));
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+// ReflectiveJaxrsScanner scanner = new ReflectiveJaxrsScanner();
+// scanner.setResourcePackage("org.apache.brooklyn.rest.api,org.apache.brooklyn.rest.apidoc,org.apache.brooklyn.rest.resources");
+// ScannerFactory.setScanner(scanner);
+ ScannerFactory.setScanner(new RestApiResourceScanner());
+
+ ServletContext context = filterConfig.getServletContext();
+ Swagger swagger = new Swagger().info(info);
+// swagger.externalDocs(new ExternalDocs("Find out more about Swagger", "http://swagger.io"));
+// swagger.securityDefinition("api_key", new ApiKeyAuthDefinition("api_key", In.HEADER));
+// swagger.securityDefinition("petstore_auth",
+// new OAuth2Definition()
+// .implicit("http://petstore.swagger.io/api/oauth/dialog")
+// .scope("read:pets", "read your pets")
+// .scope("write:pets", "modify pets in your account"));
+// swagger.tag(new Tag()
+// .name("pet")
+// .description("Everything about your Pets")
+// .externalDocs(new ExternalDocs("Find out more", "http://swagger.io")));
+// swagger.tag(new Tag()
+// .name("store")
+// .description("Access to Petstore orders"));
+// swagger.tag(new Tag()
+// .name("user")
+// .description("Operations about user")
+// .externalDocs(new ExternalDocs("Find out more about our store", "http://swagger.io")));
+
+ context.setAttribute("swagger", swagger);
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApidocResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApidocResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApidocResource.java
index e15ce80..e748516 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApidocResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApidocResource.java
@@ -21,8 +21,11 @@ package org.apache.brooklyn.rest.resources;
import io.swagger.annotations.Api;
import javax.ws.rs.Path;
-@Api(value = "org.apache.brooklyn.rest.resources.ApidocRestResource", description = "API Documentation")
+/**
+ * @author Ciprian Ciubotariu <ch...@gmx.net>
+ */
+@Api(value = "org.apache.brooklyn.rest.resources.ApidocResource", description = "API Documentation")
@Path("/v1/apidoc")
-public class ApidocResource extends org.apache.brooklyn.rest.apidoc.ApidocResource {
+public class ApidocResource extends org.apache.brooklyn.rest.apidoc.ApiListingResource {
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/webapp/WEB-INF/web.xml b/usage/rest-server/src/main/webapp/WEB-INF/web.xml
index 045eaab..1c44b02 100644
--- a/usage/rest-server/src/main/webapp/WEB-INF/web.xml
+++ b/usage/rest-server/src/main/webapp/WEB-INF/web.xml
@@ -58,7 +58,16 @@
<url-pattern>/*</url-pattern>
</filter-mapping>
- <!-- Brooklyn REST is usu run as a filter so static content can be placed in a webapp
+ <filter>
+ <filter-name>Brooklyn Swagger Bootstrap</filter-name>
+ <filter-class>org.apache.brooklyn.rest.filter.SwaggerFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>Brooklyn Swagger Bootstrap</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <!-- Brooklyn REST is usually run as a filter so static content can be placed in a webapp
to which this is added; to run as a servlet directly, replace the filter tags
below (after the comment) with the servlet tags (commented out immediately below),
(and do the same for the matching tags at the bottom)
@@ -80,7 +89,7 @@
<init-param>
<param-name>com.sun.jersey.config.property.classnames</param-name>
<param-value>
- org.apache.brooklyn.rest.apidoc.ApidocHelpMessageBodyWriter;
+ io.swagger.jaxrs.listing.SwaggerSerializers;
org.apache.brooklyn.rest.util.FormMapProvider;
org.codehaus.jackson.jaxrs.JacksonJsonProvider;
org.apache.brooklyn.rest.resources.ActivityResource;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
index 221d1ad..a2d9113 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -71,6 +71,7 @@ import com.sun.jersey.api.core.DefaultResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.eclipse.jetty.server.NetworkConnector;
+import org.apache.brooklyn.rest.filter.SwaggerFilter;
/** Convenience and demo for launching programmatically. Also used for automated tests.
* <p>
@@ -308,7 +309,7 @@ public class BrooklynRestApiLauncher {
}
public static void main(String[] args) throws Exception {
- startRestResourcesViaFilter();
+ startRestResourcesViaWebXml();
log.info("Press Ctrl-C to quit.");
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApiDocResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApiDocResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApiDocResourceTest.java
deleted file mode 100644
index e5c1f23..0000000
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApiDocResourceTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.resources;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import org.apache.brooklyn.rest.BrooklynRestApi;
-import org.apache.brooklyn.rest.apidoc.ApidocRoot;
-import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.wordnik.swagger.core.DocumentationEndPoint;
-import com.wordnik.swagger.core.DocumentationOperation;
-
-/**
- * @author Adam Lowe
- */
-@Test(singleThreaded = true)
-public class ApiDocResourceTest extends BrooklynRestResourceTest {
-
- private static final Logger log = LoggerFactory.getLogger(ApiDocResourceTest.class);
-
- @Override
- protected void addBrooklynResources() {
- for (Object o : BrooklynRestApi.getApidocResources()) {
- addResource(o);
- }
- super.addBrooklynResources();
- }
-
- @Test
- public void testRootSerializesSensibly() throws Exception {
- String data = client().resource("/v1/apidoc/").get(String.class);
- log.info("apidoc gives: "+data);
- // make sure no scala gets in
- Assert.assertFalse(data.contains("$"));
- Assert.assertFalse(data.contains("scala"));
- }
-
- @Test
- public void testCountRestResources() throws Exception {
- ApidocRoot response = client().resource("/v1/apidoc/").get(ApidocRoot.class);
- assertEquals(response.getApis().size(), 1 + Iterables.size(BrooklynRestApi.getBrooklynRestResources()));
- }
-
- @Test
- public void testEndpointSerializesSensibly() throws Exception {
- String data = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.ApidocResource").get(String.class);
- log.info("apidoc endpoint resource gives: "+data);
- // make sure no scala gets in
- Assert.assertFalse(data.contains("$"));
- Assert.assertFalse(data.contains("scala"));
- }
-
- @Test
- public void testApiDocDetails() throws Exception {
- ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.ApidocResource").get(ApidocRoot.class);
- assertEquals(countOperations(response), 2);
- }
-
- @Test
- public void testEffectorDetails() throws Exception {
- ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.EffectorResource").get(ApidocRoot.class);
- assertEquals(countOperations(response), 2);
- }
-
- @Test
- public void testEntityDetails() throws Exception {
- ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.EntityResource").get(ApidocRoot.class);
- assertEquals(countOperations(response), 14);
- }
-
- @Test
- public void testCatalogDetails() throws Exception {
- ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.CatalogResource").get(ApidocRoot.class);
- assertEquals(countOperations(response), 22, "ops="+getOperations(response));
- }
-
- @SuppressWarnings("rawtypes")
- @Test
- public void testAllAreLoadable() throws Exception {
- // sometimes -- e.g. if an annotation refers to a class name with the wrong case -- the call returns a 500 and breaks apidoc; ensure we don't trigger that.
- Map response = client().resource("/v1/apidoc/").get(Map.class);
- // "Documenation" object does not include the links :( so traverse via map
- log.debug("root doc response is: "+response);
- List apis = (List)response.get("apis");
- for (Object api: apis) {
- String link = (String) ((Map)api).get("link");
- try {
- Map r2 = client().resource(link).get(Map.class);
- log.debug("doc for "+link+" is: "+r2);
- } catch (Exception e) {
- log.error("Error in swagger/apidoc annotations, unparseable, at "+link+": "+e, e);
- Assert.fail("Error in swagger/apidoc annotations, unparseable, at "+link+": "+e, e);
- }
- }
- }
-
- /* Note in some cases we might have more than one Resource method per 'endpoint'
- */
- private int countOperations(ApidocRoot doc) throws Exception {
- return getOperations(doc).size();
- }
-
- private List<DocumentationOperation> getOperations(ApidocRoot doc) throws Exception {
- List<DocumentationOperation> result = Lists.newArrayList();
- for (DocumentationEndPoint endpoint : doc.getApis()) {
- result.addAll(endpoint.getOperations());
- }
- return result;
- }
-}
-
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java
new file mode 100644
index 0000000..c98117f
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.rest.BrooklynRestApi;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+/**
+ * @author Adam Lowe
+ */
+@Test(singleThreaded = true)
+public class ApidocResourceTest extends BrooklynRestResourceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(ApidocResourceTest.class);
+
+ @Override
+ protected void addBrooklynResources() {
+ for (Object o : BrooklynRestApi.getApidocResources()) {
+ addResource(o);
+ }
+ super.addBrooklynResources();
+ }
+
+ @Test
+ public void testRootSerializesSensibly() throws Exception {
+ String data = client().resource("/v1/apidoc/swagger.json").get(String.class);
+ log.info("apidoc gives: "+data);
+ // make sure no scala gets in
+ Assert.assertFalse(data.contains("$"));
+ Assert.assertFalse(data.contains("scala"));
+ }
+
+// @Test
+// public void testCountRestResources() throws Exception {
+// ApidocRoot response = client().resource("/v1/apidoc/").get(ApidocRoot.class);
+// assertEquals(response.getApis().size(), 1 + Iterables.size(BrooklynRestApi.getBrooklynRestResources()));
+// }
+//
+// @Test
+// public void testEndpointSerializesSensibly() throws Exception {
+// String data = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.ApidocResource").get(String.class);
+// log.info("apidoc endpoint resource gives: "+data);
+// // make sure no scala gets in
+// Assert.assertFalse(data.contains("$"));
+// Assert.assertFalse(data.contains("scala"));
+// }
+//
+// @Test
+// public void testApiDocDetails() throws Exception {
+// ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.ApidocResource").get(ApidocRoot.class);
+// assertEquals(countOperations(response), 2);
+// }
+//
+// @Test
+// public void testEffectorDetails() throws Exception {
+// ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.EffectorResource").get(ApidocRoot.class);
+// assertEquals(countOperations(response), 2);
+// }
+//
+// @Test
+// public void testEntityDetails() throws Exception {
+// ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.EntityResource").get(ApidocRoot.class);
+// assertEquals(countOperations(response), 14);
+// }
+//
+// @Test
+// public void testCatalogDetails() throws Exception {
+// ApidocRoot response = client().resource("/v1/apidoc/org.apache.brooklyn.rest.resources.CatalogResource").get(ApidocRoot.class);
+// assertEquals(countOperations(response), 22, "ops="+getOperations(response));
+// }
+
+ @SuppressWarnings("rawtypes")
+ @Test
+ public void testAllAreLoadable() throws Exception {
+ // sometimes -- e.g. if an annotation refers to a class name with the wrong case -- the call returns a 500 and breaks apidoc; ensure we don't trigger that.
+ Map response = client().resource("/v1/apidoc/swagger.json").get(Map.class);
+ // "Documenation" object does not include the links :( so traverse via map
+ log.debug("root doc response is: "+response);
+ List apis = (List)response.get("apis");
+ for (Object api: apis) {
+ String link = (String) ((Map)api).get("link");
+ try {
+ Map r2 = client().resource(link).get(Map.class);
+ log.debug("doc for "+link+" is: "+r2);
+ } catch (Exception e) {
+ log.error("Error in swagger/apidoc annotations, unparseable, at "+link+": "+e, e);
+ Assert.fail("Error in swagger/apidoc annotations, unparseable, at "+link+": "+e, e);
+ }
+ }
+ }
+
+// /* Note in some cases we might have more than one Resource method per 'endpoint'
+// */
+// private int countOperations(ApidocRoot doc) throws Exception {
+// return getOperations(doc).size();
+// }
+//
+// private List<DocumentationOperation> getOperations(ApidocRoot doc) throws Exception {
+// List<DocumentationOperation> result = Lists.newArrayList();
+// for (DocumentationEndPoint endpoint : doc.getApis()) {
+// result.addAll(endpoint.getOperations());
+// }
+// return result;
+// }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
index 2f50cfc..27115ad 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -36,9 +36,11 @@ import com.sun.jersey.api.core.DefaultResourceConfig;
import com.sun.jersey.test.framework.AppDescriptor;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.LowLevelAppDescriptor;
+import com.sun.jersey.test.framework.WebAppDescriptor;
import org.apache.brooklyn.rest.BrooklynRestApi;
import org.apache.brooklyn.rest.BrooklynRestApiLauncherTest;
+import org.apache.brooklyn.rest.filter.SwaggerFilter;
import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
import org.apache.brooklyn.rest.util.NullHttpServletRequestProvider;
import org.apache.brooklyn.rest.util.NullServletConfigProvider;
@@ -157,6 +159,12 @@ public abstract class BrooklynRestApiTest {
jerseyTest = new JerseyTest() {
@Override
protected AppDescriptor configure() {
+// return new WebAppDescriptor.Builder(
+// "io.swagger.jaxrs.listing",
+// "org.apache.brooklyn.rest.util",
+// "org.codehaus.jackson.jaxrs",
+// "org.apache.brooklyn.rest.resources")
+// .filterClass(SwaggerFilter.class).build();
return new LowLevelAppDescriptor.Builder(config).build();
}
};
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java
index 06c60ea..106780d 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java
@@ -26,6 +26,7 @@ import javax.ws.rs.ext.Provider;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
+import com.sun.jersey.spi.container.servlet.WebConfig;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
@@ -37,6 +38,10 @@ public class NullServletConfigProvider implements InjectableProvider<Context, Ty
return new Injectable<ServletContext>() {
public ServletContext getValue() { return null; }
};
+ } else if (WebConfig.class == c) {
+ return new Injectable<ServletContext>() {
+ public ServletContext getValue() { return null; }
+ };
} else
return null;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/pom.xml
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/pom.xml b/utils/rest-swagger/pom.xml
index 42edc4b..42e9767 100644
--- a/utils/rest-swagger/pom.xml
+++ b/utils/rest-swagger/pom.xml
@@ -40,11 +40,7 @@
<!-- ATTN: this moves jersey-server from 1.7 to 1.12 -->
<dependency>
<groupId>com.sun.jersey</groupId>
- <artifactId>jersey-server</artifactId>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-core</artifactId>
+ <artifactId>jersey-servlet</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@@ -83,16 +79,18 @@
<artifactId>swagger-jaxrs</artifactId>
<exclusions>
<exclusion>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- </exclusion>
- <exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ </dependency>
+
+
</dependencies>
</project>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApiListingResource.java
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApiListingResource.java b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApiListingResource.java
new file mode 100644
index 0000000..3edffe9
--- /dev/null
+++ b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApiListingResource.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.brooklyn.rest.apidoc;
+
+import com.sun.jersey.spi.container.servlet.WebConfig;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.config.FilterFactory;
+import io.swagger.config.Scanner;
+import io.swagger.config.ScannerFactory;
+import io.swagger.config.SwaggerConfig;
+import io.swagger.core.filter.SpecFilter;
+import io.swagger.core.filter.SwaggerSpecFilter;
+import io.swagger.jaxrs.Reader;
+import io.swagger.jaxrs.config.JaxrsScanner;
+import io.swagger.jaxrs.config.ReaderConfigUtils;
+import io.swagger.jaxrs.listing.SwaggerSerializers;
+import io.swagger.models.Swagger;
+import io.swagger.util.Yaml;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+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.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ApiListingResource usable within a jersey servlet filter.
+ *
+ * Taken from io.swagger:swagger-jaxrs, class
+ * io.swagger.jaxrs.listing.ApiListingResource, which can only be used within a
+ * servlet context. We are here using a filter, but jersey has a WebConfig class
+ * that can substitute ServletConfig and FilterConfig.
+ *
+ * @todo Remove when the rest-server is no longer running within a filter (e.g.
+ * as a standalone OSGi http service)
+ *
+ * @author Ciprian Ciubotariu <ch...@gmx.net>
+ */
+public class ApiListingResource {
+
+ static Logger LOGGER = LoggerFactory.getLogger(ApiListingResource.class);
+
+ @Context
+ ServletContext context;
+
+ boolean initialized = false;
+
+ private static class ServletConfigAdapter implements ServletConfig {
+
+ private final WebConfig webConfig;
+
+ private ServletConfigAdapter(WebConfig webConfig) {
+ this.webConfig = webConfig;
+ }
+
+ @Override
+ public String getServletName() {
+ return webConfig.getName();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return webConfig.getServletContext();
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return webConfig.getInitParameter(name);
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return webConfig.getInitParameterNames();
+ }
+
+ }
+
+ protected synchronized Swagger scan(Application app, WebConfig sc) {
+ Swagger swagger = null;
+ Scanner scanner = ScannerFactory.getScanner();
+ LOGGER.debug("using scanner " + scanner);
+
+ if (scanner != null) {
+ SwaggerSerializers.setPrettyPrint(scanner.getPrettyPrint());
+ swagger = (Swagger) context.getAttribute("swagger");
+
+ Set<Class<?>> classes;
+ if (scanner instanceof JaxrsScanner) {
+ JaxrsScanner jaxrsScanner = (JaxrsScanner) scanner;
+ classes = jaxrsScanner.classesFromContext(app, new ServletConfigAdapter(sc));
+ } else {
+ classes = scanner.classes();
+ }
+ if (classes != null) {
+ Reader reader = new Reader(swagger, ReaderConfigUtils.getReaderConfig(context));
+ swagger = reader.read(classes);
+ if (scanner instanceof SwaggerConfig) {
+ swagger = ((SwaggerConfig) scanner).configure(swagger);
+ } else {
+ SwaggerConfig configurator = (SwaggerConfig) context.getAttribute("reader");
+ if (configurator != null) {
+ LOGGER.debug("configuring swagger with " + configurator);
+ configurator.configure(swagger);
+ } else {
+ LOGGER.debug("no configurator");
+ }
+ }
+ context.setAttribute("swagger", swagger);
+ }
+ }
+ initialized = true;
+ return swagger;
+ }
+
+ private Swagger process(
+ Application app,
+ WebConfig sc,
+ HttpHeaders headers,
+ UriInfo uriInfo) {
+ Swagger swagger = (Swagger) context.getAttribute("swagger");
+ if (!initialized) {
+ swagger = scan(app, sc);
+ }
+ if (swagger != null) {
+ SwaggerSpecFilter filterImpl = FilterFactory.getFilter();
+ if (filterImpl != null) {
+ SpecFilter f = new SpecFilter();
+ swagger = f.filter(swagger, filterImpl, getQueryParams(uriInfo.getQueryParameters()), getCookies(headers),
+ getHeaders(headers));
+ }
+ }
+ return swagger;
+ }
+
+ @GET
+ @Produces({MediaType.APPLICATION_JSON, "application/yaml"})
+ @ApiOperation(value = "The swagger definition in either JSON or YAML", hidden = true)
+ @Path("/swagger.{type:json|yaml}")
+ public Response getListing(
+ @Context Application app,
+ @Context WebConfig sc,
+ @Context HttpHeaders headers,
+ @Context UriInfo uriInfo,
+ @PathParam("type") String type) {
+ if (StringUtils.isNotBlank(type) && type.trim().equalsIgnoreCase("yaml")) {
+ return getListingYaml(app, sc, headers, uriInfo);
+ } else {
+ return getListingJson(app, sc, headers, uriInfo);
+ }
+ }
+
+ @GET
+ @Produces({MediaType.APPLICATION_JSON})
+ @Path("/swagger")
+ @ApiOperation(value = "The swagger definition in JSON", hidden = true)
+ public Response getListingJson(
+ @Context Application app,
+ @Context WebConfig sc,
+ @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+ Swagger swagger = process(app, sc, headers, uriInfo);
+
+ if (swagger != null) {
+ return Response.ok().entity(swagger).build();
+ } else {
+ return Response.status(404).build();
+ }
+ }
+
+ @GET
+ @Produces("application/yaml")
+ @Path("/swagger")
+ @ApiOperation(value = "The swagger definition in YAML", hidden = true)
+ public Response getListingYaml(
+ @Context Application app,
+ @Context WebConfig sc,
+ @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+ Swagger swagger = process(app, sc, headers, uriInfo);
+ try {
+ if (swagger != null) {
+ String yaml = Yaml.mapper().writeValueAsString(swagger);
+ StringBuilder b = new StringBuilder();
+ String[] parts = yaml.split("\n");
+ for (String part : parts) {
+ b.append(part);
+ b.append("\n");
+ }
+ return Response.ok().entity(b.toString()).type("application/yaml").build();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return Response.status(404).build();
+ }
+
+ protected Map<String, List<String>> getQueryParams(MultivaluedMap<String, String> params) {
+ Map<String, List<String>> output = new HashMap<>();
+ if (params != null) {
+ for (String key : params.keySet()) {
+ List<String> values = params.get(key);
+ output.put(key, values);
+ }
+ }
+ return output;
+ }
+
+ protected Map<String, String> getCookies(HttpHeaders headers) {
+ Map<String, String> output = new HashMap<>();
+ if (headers != null) {
+ for (String key : headers.getCookies().keySet()) {
+ Cookie cookie = headers.getCookies().get(key);
+ output.put(key, cookie.getValue());
+ }
+ }
+ return output;
+ }
+
+ protected Map<String, List<String>> getHeaders(HttpHeaders headers) {
+ Map<String, List<String>> output = new HashMap<>();
+ if (headers != null) {
+ for (String key : headers.getRequestHeaders().keySet()) {
+ List<String> values = headers.getRequestHeaders().get(key);
+ output.put(key, values);
+ }
+ }
+ return output;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocEndpoint.java
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocEndpoint.java b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocEndpoint.java
deleted file mode 100644
index 9260c26..0000000
--- a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocEndpoint.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.apidoc;
-
-import java.util.Comparator;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import com.wordnik.swagger.core.DocumentationEndPoint;
-
-@JsonIgnoreProperties({
- "com$wordnik$swagger$core$DocumentationEndPoint$$ops"
-})
-public class ApidocEndpoint extends DocumentationEndPoint {
-
- public static final Comparator<ApidocEndpoint> COMPARATOR = new Comparator<ApidocEndpoint>() {
- @Override
- public int compare(ApidocEndpoint o1, ApidocEndpoint o2) {
- if (o1.name==o2.name) return 0;
- if (o1.name==null) return -1;
- if (o2.name==null) return 1;
- return o1.name.compareTo(o2.name);
- }
- };
-
- public final String name;
- public final String link;
-
- @JsonCreator
- public ApidocEndpoint(@JsonProperty("name") String name, @JsonProperty("path") String path, @JsonProperty("description") String description, @JsonProperty("link") String link) {
- super(path, description);
- this.name = name;
- this.link = link;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocHelpMessageBodyWriter.java
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocHelpMessageBodyWriter.java b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocHelpMessageBodyWriter.java
deleted file mode 100644
index 12114e6..0000000
--- a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocHelpMessageBodyWriter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.apidoc;
-
-import javax.ws.rs.ext.Provider;
-
-import com.wordnik.swagger.jaxrs.ApiHelpMessageBodyWriter;
-
-/** subclassed for convenience */
-@Provider
-public class ApidocHelpMessageBodyWriter extends ApiHelpMessageBodyWriter {
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocResource.java
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocResource.java b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocResource.java
deleted file mode 100644
index 2c0330e..0000000
--- a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocResource.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.apidoc;
-
-import org.apache.brooklyn.swagger.annotations.Apidoc;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-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.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-
-import com.google.common.collect.ImmutableList;
-import com.sun.jersey.api.core.ResourceConfig;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.core.Documentation;
-import io.swagger.core.DocumentationEndPoint;
-import io.swagger.jaxrs.ConfigReader;
-import io.swagger.jaxrs.HelpApi;
-import io.swagger.jaxrs.JaxrsApiReader;
-import io.swagger.jaxrs.JaxrsApiSpecParser;
-
-@Produces({"application/json"})
-/** create a concrete subclass for this annotated with the Path where
- * this resource should live
- * <p>
- * like Swagger ApiListing (and based on that) but:
- * supports singletons as well as classes;
- * supports simpler Apidoc annotation (doesn't repeat path, in common case);
- * doesn't support listingPath/Class that swagger does (but describes in under /apidoc/name.of.Class
- * does not support auth filters
- */
-abstract public class ApidocResource {
-
- static ConfigReader configReader;
- static {
- JaxrsApiReader.setFormatString("");
- }
-
- protected boolean isSupportedMediaType(String type) {
- return "application/json".equals(type) || "application/xml".equals(type);
- }
-
- protected boolean isIncludedForDocumentation(Class<?> resource) {
- // TODO currently only support @Produces, not Contenty-type header, or Accept header (which original ApiListing does support)
- Produces produces = getAnnotation(resource, Produces.class);
- if (produces == null) return false;
- for (String type: produces.value())
- if (isSupportedMediaType(type))
- return true;
- return false;
- }
-
- protected <A extends Annotation> A getAnnotation(Class<?> r, Class<A> annotationClass) {
- A result = r.getAnnotation(annotationClass);
- if (result == null) {
- // first look at things directly on superclass (not inherited)
- if (r.getSuperclass()!=null)
- result = r.getSuperclass().getAnnotation(annotationClass);
- }
- if (result == null) {
- // then look at interfaces here (not inherited)
- // we look at superclasses next so don't have to here
- for(Class<?> parentInterface : r.getInterfaces()) {
- result = parentInterface.getAnnotation(annotationClass);
- if (result != null) break;
- }
- }
- if (result == null) {
- // lastly take annotations on superclass and interfaces on superclass, recursively
- // (so in short we prefer things lower down)
- if (r.getSuperclass()!=null)
- result = getAnnotation(r.getSuperclass(), annotationClass);
- }
- return result;
- }
-
- protected String getLinkFor(String path, Class<?> resource) {
- return getClass().getAnnotation(Path.class).value()+"/"+getLinkWordFor(resource);
- }
-
- protected String getLinkWordFor(Class<?> resource) {
- if (resource.getCanonicalName()!=null)
- return resource.getCanonicalName();
- else
- return Integer.toHexString(resource.hashCode());
- }
-
- protected Class<?> getResourceOfLink(ResourceConfig rc, String link) {
- for (Class<?> r: getResourceClasses(rc)) {
- if (getLinkWordFor(r).equals(link))
- return r;
- }
- return null;
- }
-
- @GET
- @ApiOperation(value = "Returns list of all available API resource endpoints",
- response = ApidocRoot.class,
- responseContainer = "List")
- public Response getAllApis(
- @Context ResourceConfig rc,
- @Context HttpHeaders headers,
- @Context UriInfo uriInfo) {
-
- String apiVersion = getConfigReader().getApiVersion();
- String swaggerVersion = getConfigReader().getSwaggerVersion();
- String basePath = getConfigReader().getBasePath();
-
- Set<Class<?>> resources = getResourceClasses(rc);
-
- ApidocRoot allApiDoc = new ApidocRoot();
-
- List<ApidocEndpoint> endpoints = new ArrayList<>();
- for (Class<?> resource : resources) {
- if (!isIncludedForDocumentation(resource))
- continue;
-
- Apidoc apidoc = getAnnotation(resource, Apidoc.class);
- Api apidocX = getAnnotation(resource, Api.class);
- Path rsPath = getAnnotation(resource, Path.class);
-
- if (apidoc==null && apidocX == null) continue;
- String path = rsPath.value();
- String name = null;
- String description;
-
- if (apidoc!=null) {
- name = apidoc.value();
- description = apidoc.description();
- } else {
- path = apidocX.value();
- description = apidocX.description();
- }
-
- endpoints.add(new ApidocEndpoint(name, path, description, getLinkFor(path, resource)));
- }
-
- Collections.sort(endpoints, ApidocEndpoint.COMPARATOR);
-
- for (ApidocEndpoint api: endpoints) {
- if (!isApiAdded(allApiDoc, api)) {
- allApiDoc.addApi(api);
- }
- }
- allApiDoc.setSwaggerVersion(swaggerVersion);
- allApiDoc.setBasePath(basePath);
- allApiDoc.setApiVersion(apiVersion);
-
- return Response.ok().entity(allApiDoc).build();
- }
-
- protected Set<Class<?>> getResourceClasses(ResourceConfig rc) {
- Set<Class<?>> resourceClasses = rc.getRootResourceClasses();
- Set<Object> resourceObjects = rc.getRootResourceSingletons();
-
- Set<Class<?>> resources = new LinkedHashSet<Class<?>>();
- // @Path should always be set, right? unless something is oddd
- for (Class<?> r: resourceClasses)
- if (r.getAnnotation(Path.class)!=null) resources.add(r);
- for (Object r: resourceObjects) {
- if (getAnnotation(r.getClass(), Path.class)!=null) {
- resources.add(r.getClass());
- }
- }
- return resources;
- }
-
- private boolean isApiAdded(Documentation allApiDoc, DocumentationEndPoint endpoint) {
- boolean isAdded = false;
- if (allApiDoc.getApis() != null) {
- for (DocumentationEndPoint addedApi : allApiDoc.getApis()) {
- if (endpoint.getPath().equals(addedApi.getPath())) isAdded = true;
- }
- }
- return isAdded;
- }
-
- @GET
- @Path("/{resource}")
- @ApiOperation(value = "Returns detail on the given API resource endpoint",
- response = DocumentationEndPoint.class,
- responseContainer = "List")
- public Response details(
- @Context ResourceConfig rc,
- @Context HttpHeaders headers,
- @Context UriInfo uriInfo,
- @PathParam("resource") String resource) {
- Class<?> target = getResourceOfLink(rc, resource);
- if (target==null) return Response.status(Response.Status.NOT_FOUND).build();
-
- // roughly duplicates JavaHelp
- String apiVersion = getConfigReader().getApiVersion();
- String swaggerVersion = getConfigReader().getSwaggerVersion();
- String basePath = getConfigReader().getBasePath();
-
- String apiFilterClassName = getConfigReader().getApiFilterClassName();
-
- Apidoc apidoc = getAnnotation(target, Apidoc.class);
- Api apidocX = getAnnotation(target, Api.class);
- Path rsPath = getAnnotation(target, Path.class);
-
- if ((apidoc==null && apidocX==null) || rsPath==null)
- return Response.status(Response.Status.NOT_FOUND).build();
-
- String apiPath = apidoc!=null ? rsPath.value() : apidocX.value();
-
- HelpApi helpApi = new HelpApi(apiFilterClassName);
- Documentation doc = read(target, apiVersion, swaggerVersion, basePath, apiPath);
- Documentation docs = helpApi.filterDocs(doc, headers, uriInfo, apiPath, apiPath);
- return Response.ok().entity(docs).build();
- }
-
-
-
-
- // items below here simply override the swagger Jaxrs* classes/behaviour so we can use @Path/@Apidoc instead of @Api
-
- protected ConfigReader getConfigReader() {
- if (configReader==null) configReader = new ConfigReader(null);
- return configReader;
- }
-
- static protected Map<Class<?>,Documentation> endpointsCache = new LinkedHashMap<Class<?>, Documentation>();
-
- protected Documentation read(Class<?> target, String apiVersion, String swaggerVersion, String basePath, String apiPath) {
- Documentation result = endpointsCache.get(target);
- if (result!=null) return result;
- JaxrsApiSpecParser parser = new ApidocJaxrsSpecParser(target, apiVersion, swaggerVersion, basePath, apiPath);
- result = parser.parse();
- endpointsCache.put(target, result);
- return result;
- }
-
- @Api("ignored")
- static class ApidocJaxrsSpecParser extends JaxrsApiSpecParser {
- public ApidocJaxrsSpecParser(Class<?> target, String apiVersion, String swaggerVersion, String basePath, String apiPath) {
- super(target, apiVersion, swaggerVersion, basePath, apiPath);
- }
- @Override
- public Api apiEndpoint() {
- // return an ignored item; all clients do is check it isn't null
- return ApidocJaxrsSpecParser.class.getAnnotation(Api.class);
- }
- @Override
- public Class<?> hostClass() {
- // Overriding to make sure we have a look at the interfaces (Jersey jaxrs implementation doesn't bother)
- // Note this means we require the @Path class annotation on the same class as all the method annotations
- for (Class<?> tryMe : ImmutableList.<Class<?>>builder().add(super.hostClass()).add(super.hostClass().getInterfaces()).build()) {
- if (tryMe.getAnnotation(Path.class) != null) {
- return tryMe;
- }
- }
- return super.hostClass();
- }
-
- public String getPath(Method method) {
- Path cwsPath = hostClass().getAnnotation(Path.class);
- Path mwsPath = method.getAnnotation(Path.class);
- if (cwsPath==null) return null;
- return cwsPath.value() + (mwsPath!=null ? mwsPath.value() : "");
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocRoot.java
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocRoot.java b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocRoot.java
deleted file mode 100644
index e88f1ed..0000000
--- a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/ApidocRoot.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.apidoc;
-
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonSetter;
-
-import io.swagger.core.Documentation;
-
-@JsonIgnoreProperties({
- "com$wordnik$swagger$core$Documentation$$apis",
- "com$wordnik$swagger$core$Documentation$$models"
-})
-public class ApidocRoot extends Documentation {
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @JsonProperty("apis")
- public List<ApidocEndpoint> getApidocApis() {
- return (List) getApis();
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @JsonSetter("apis")
- public void setApidocApis(List<ApidocEndpoint> ep) {
- super.setApis((List)ep);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/RestApiResourceScanner.java
----------------------------------------------------------------------
diff --git a/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/RestApiResourceScanner.java b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/RestApiResourceScanner.java
new file mode 100644
index 0000000..2f3c8c7
--- /dev/null
+++ b/utils/rest-swagger/src/main/java/org/apache/brooklyn/rest/apidoc/RestApiResourceScanner.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.brooklyn.rest.apidoc;
+
+import com.google.common.collect.Sets;
+import io.swagger.annotations.Api;
+import io.swagger.jaxrs.config.AbstractScanner;
+import io.swagger.jaxrs.config.JaxrsScanner;
+import java.util.HashSet;
+import java.util.Set;
+import javax.servlet.ServletConfig;
+import javax.ws.rs.core.Application;
+import org.apache.brooklyn.util.collections.MutableSet;
+
+/**
+ * Much like DefaultJaxrsScanner, but looks at annotations of ancestors as well.
+ *
+ * For instance, if a resource implementation exposes an annotated interface,
+ * that interface will be added as well.
+ *
+ * @author Ciprian Ciubotariu <ch...@gmx.net>
+ */
+public class RestApiResourceScanner extends AbstractScanner implements JaxrsScanner {
+
+ private Set<Class<?>> apiClasses = null;
+
+
+ private void addAnnotatedClasses(Set<Class<?>> output, Set<Class<?>> classes) {
+ for (Class<?> clz : classes) {
+ if (clz.getAnnotation(Api.class) != null) {
+ output.add(clz);
+ }
+ addAnnotatedClasses(output, Sets.newHashSet(clz.getInterfaces()));
+ }
+ }
+
+ private synchronized void buildApiClasses(Application app) {
+ if (apiClasses == null) {
+ apiClasses = new HashSet<>();
+ if (app != null) {
+ Set<Class<?>> classes = app.getClasses();
+ if (classes != null) {
+ addAnnotatedClasses(apiClasses, classes);
+ }
+ Set<Object> singletons = app.getSingletons();
+ if (singletons != null) {
+ for (Object o : singletons) {
+ addAnnotatedClasses(apiClasses, (MutableSet<Class<?>>) MutableSet.of(o.getClass()));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public Set<Class<?>> classesFromContext(Application app, ServletConfig sc) {
+ buildApiClasses(app);
+ return apiClasses;
+ }
+
+ @Override
+ public Set<Class<?>> classes() {
+ return new HashSet<>();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/swagger-annotations/pom.xml
----------------------------------------------------------------------
diff --git a/utils/swagger-annotations/pom.xml b/utils/swagger-annotations/pom.xml
new file mode 100644
index 0000000..36e5959
--- /dev/null
+++ b/utils/swagger-annotations/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>brooklyn-swagger-annotations</artifactId>
+ <name>Brooklyn Swagger Annotations</name>
+
+ <description>
+ Swagger-like annotations developed for Brooklyn but not dependendent on Brooklyn
+ </description>
+
+ <parent>
+ <groupId>org.apache.brooklyn</groupId>
+ <artifactId>brooklyn-parent</artifactId>
+ <version>0.9.0-SNAPSHOT</version> <!-- BROOKLYN_VERSION -->
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.wordnik</groupId>
+ <artifactId>swagger-core_2.9.1</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5c625626/utils/swagger-annotations/src/main/java/org/apache/brooklyn/swagger/annotations/Apidoc.java
----------------------------------------------------------------------
diff --git a/utils/swagger-annotations/src/main/java/org/apache/brooklyn/swagger/annotations/Apidoc.java b/utils/swagger-annotations/src/main/java/org/apache/brooklyn/swagger/annotations/Apidoc.java
new file mode 100644
index 0000000..a9263c2
--- /dev/null
+++ b/utils/swagger-annotations/src/main/java/org/apache/brooklyn/swagger/annotations/Apidoc.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.swagger.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** like Swagger Api annotation (and treated similarly) but doesn't require path to be repeated, and supports a name */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Apidoc {
+ String value();
+ String description() default "";
+ // ? what is 'open' in @Api
+}