summaryrefslogtreecommitdiff
path: root/qpid/java
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2015-03-16 01:31:54 +0000
committerRobert Godfrey <rgodfrey@apache.org>2015-03-16 01:31:54 +0000
commit1fa496fbf25aa9b30c61a0b73ded9549d407aa3c (patch)
tree27b3ee697650e316cc58bb6985e7ccd133d4fb6f /qpid/java
parent3385198ba2f67d14ebf2ab73721e6ab76c112efa (diff)
downloadqpid-python-1fa496fbf25aa9b30c61a0b73ded9549d407aa3c.tar.gz
QPID-6454 : [Java Broker] Provide mechanism to view REST API interface definition
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1666850 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java')
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java11
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/ApiDocsServlet.java355
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MetaDataServlet.java9
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/css/apidocs.css52
4 files changed, 422 insertions, 5 deletions
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index 110be691bb..5020ffe3cd 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -64,6 +64,7 @@ import org.apache.qpid.server.management.plugin.filter.RedirectingAuthorisationF
import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet;
import org.apache.qpid.server.management.plugin.servlet.FileServlet;
import org.apache.qpid.server.management.plugin.servlet.LogFileServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.ApiDocsServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.HelperServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.LogFileListingServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet;
@@ -278,6 +279,7 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
FilterHolder restAuthorizationFilter = new FilterHolder(new ForbiddingAuthorisationFilter());
restAuthorizationFilter.setInitParameter(ForbiddingAuthorisationFilter.INIT_PARAM_ALLOWED, "/service/sasl");
root.addFilter(restAuthorizationFilter, "/api/*", EnumSet.of(DispatcherType.REQUEST));
+ root.addFilter(restAuthorizationFilter, "/apidocs/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(restAuthorizationFilter, "/service/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(new FilterHolder(new RedirectingAuthorisationFilter()), HttpManagementUtil.ENTRY_POINT_PATH, EnumSet.of(DispatcherType.REQUEST));
root.addFilter(new FilterHolder(new RedirectingAuthorisationFilter()), "/index.html", EnumSet.of(DispatcherType.REQUEST));
@@ -316,7 +318,7 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
root.addServlet(new ServletHolder(new LogRecordsServlet()), "/service/logrecords");
- root.addServlet(new ServletHolder(new MetaDataServlet()), "/service/metadata");
+ root.addServlet(new ServletHolder(new MetaDataServlet(getModel())), "/service/metadata");
root.addServlet(new ServletHolder(new SaslServlet()), "/service/sasl");
@@ -454,6 +456,13 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
getContextValue(Integer.class, MAX_HTTP_FILE_UPLOAD_SIZE_CONTEXT_NAME)));
root.addServlet(servletHolder, "/api/latest/" + name + "/*");
root.addServlet(servletHolder, "/api/v" + BrokerModel.MODEL_MAJOR_VERSION + "/" + name + "/*");
+ ServletHolder docServletHolder = new ServletHolder(name+"docs", new ApiDocsServlet(getModel(),hierarchy));
+ root.addServlet(docServletHolder, "/apidocs/latest/" + name + "/");
+ root.addServlet(docServletHolder, "/apidocs/v" + BrokerModel.MODEL_MAJOR_VERSION + "/" + name +"/");
+ root.addServlet(docServletHolder, "/apidocs/latest/" + name );
+ root.addServlet(docServletHolder, "/apidocs/v" + BrokerModel.MODEL_MAJOR_VERSION + "/" + name);
+
+
}
private void logOperationalListenMessages(Collection<Port<?>> ports)
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/ApiDocsServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/ApiDocsServlet.java
new file mode 100644
index 0000000000..817fab7689
--- /dev/null
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/ApiDocsServlet.java
@@ -0,0 +1,355 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.qpid.server.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.model.AbstractConfiguredObject;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectAttribute;
+import org.apache.qpid.server.model.IllegalStateTransitionException;
+import org.apache.qpid.server.model.IntegrityViolationException;
+import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.util.urlstreamhandler.data.Handler;
+import org.apache.qpid.server.virtualhost.ExchangeExistsException;
+import org.apache.qpid.server.virtualhost.QueueExistsException;
+import org.apache.qpid.util.DataUrlUtils;
+
+public class ApiDocsServlet extends AbstractServlet
+{
+ private static final Logger LOGGER = LoggerFactory.getLogger(ApiDocsServlet.class);
+
+ public static final String DEPTH_PARAM = "depth";
+ public static final String OVERSIZE_PARAM = "oversize";
+ public static final String ACTUALS_PARAM = "actuals";
+ public static final String SORT_PARAM = "sort";
+ public static final String INCLUDE_SYS_CONTEXT_PARAM = "includeSysContext";
+ public static final String INHERITED_ACTUALS_PARAM = "inheritedActuals";
+ public static final String EXTRACT_INITIAL_CONFIG_PARAM = "extractInitialConfig";
+ public static final int SC_UNPROCESSABLE_ENTITY = 422;
+
+ /**
+ * Signifies that the agent wishes the servlet to set the Content-Disposition on the
+ * response with the value attachment. This filename will be derived from the parameter value.
+ */
+ public static final String CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM = "contentDispositionAttachmentFilename";
+
+ public static final Set<String> RESERVED_PARAMS =
+ new HashSet<>(Arrays.asList(DEPTH_PARAM,
+ SORT_PARAM,
+ OVERSIZE_PARAM,
+ ACTUALS_PARAM,
+ INCLUDE_SYS_CONTEXT_PARAM,
+ EXTRACT_INITIAL_CONFIG_PARAM,
+ INHERITED_ACTUALS_PARAM,
+ CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM));
+ private final Model _model;
+ private final Collection<Class<? extends ConfiguredObject>> _types;
+
+ private Class<? extends ConfiguredObject>[] _hierarchy;
+
+ private static final Set<Character> VOWELS = new HashSet<>(Arrays.asList('a','e','i','o','u'));
+
+
+ public ApiDocsServlet(final Model model, Class<? extends ConfiguredObject>... hierarchy)
+ {
+ super();
+ _model = model;
+ _hierarchy = hierarchy;
+ _types = _model.getTypeRegistry().getTypeSpecialisations(getConfiguredClass());
+
+ }
+
+ @Override
+ protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.setContentType("text/html");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+
+ PrintWriter writer = response.getWriter();
+ writePreamble(writer);
+ writeHead(writer);
+ writeUsage(writer, request);
+ writeTypes(writer);
+ writeAttributes(writer);
+ writeFoot(writer);
+ }
+
+ private void writePreamble(final PrintWriter writer)
+ {
+ writer.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
+ writer.println("\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
+ writer.println("<html>");
+ writer.println("<body>");
+
+ }
+
+ private void writeHead(final PrintWriter writer)
+ {
+ writer.println("<head>");
+ writer.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/apidocs.css\">");
+ writer.print("<title>");
+ writer.print("Qpid API : " + getConfiguredClass().getSimpleName());
+ writer.println("</title>");
+
+ writer.println("</head>");
+ }
+
+ private void writeUsage(final PrintWriter writer, final HttpServletRequest request)
+ {
+ writer.println("<a name=\"usage\"><h1>Usage</h1></a>");
+ writer.println("<table class=\"usage\">");
+ writer.println("<tbody>");
+ writer.print("<tr><th class=\"operation\">Read</th><td class=\"method\">GET</td><td class=\"path\">" + request.getServletPath()
+ .replace("apidocs", "api"));
+
+ for (final Class<? extends ConfiguredObject> category : _hierarchy)
+ {
+ writer.print("[/&lt;" + category.getSimpleName().toLowerCase() + " name or id&gt;");
+ }
+ for(int i = 0; i < _hierarchy.length; i++)
+ {
+ writer.print("] ");
+ }
+ writer.println("</td></tr>");
+
+ writer.print("<tr><th class=\"operation\">Update</th><td class=\"method\">PUT or POST</td><td class=\"path\">"
+ + request.getServletPath().replace("apidocs", "api"));
+ for (final Class<? extends ConfiguredObject> category : _hierarchy)
+ {
+ writer.print("/&lt;" + category.getSimpleName().toLowerCase() + " name or id&gt;");
+ }
+
+ if(_hierarchy.length>1)
+ {
+ writer.print(
+ "<tr><th class=\"operation\">Create</th><td class=\"method\">PUT or POST</td><td class=\"path\">"
+ + request.getServletPath().replace("apidocs", "api"));
+ for (int i = 0; i < _hierarchy.length - 1; i++)
+ {
+ writer.print("/&lt;" + _hierarchy[i].getSimpleName().toLowerCase() + " name or id&gt;");
+ }
+
+ writer.print("<tr><th class=\"operation\">Delete</th><td class=\"method\">DELETE</td><td class=\"path\">"
+ + request.getServletPath().replace("apidocs", "api"));
+ for (final Class<? extends ConfiguredObject> category : _hierarchy)
+ {
+ writer.print("/&lt;" + category.getSimpleName().toLowerCase() + " name or id&gt;");
+ }
+ }
+
+ writer.println("</tbody>");
+ writer.println("</table>");
+
+ }
+
+
+ private void writeTypes(final PrintWriter writer)
+ {
+ if(!_types.isEmpty() && !(_types.size() == 1 && getTypeName(_types.iterator().next()).trim().equals("")))
+ {
+ writer.println("<a name=\"types\"><h2>Types</h2></a>");
+ writer.println("<table class=\"types\">");
+ writer.println("<thead>");
+ writer.println("<tr><th class=\"type\">Type</th><th class=\"description\">Description</th></tr>");
+ writer.println("</thead>");
+
+ writer.println("<tbody>");
+ for (Class<? extends ConfiguredObject> type : _types)
+ {
+ writer.print("<tr><td class=\"type\">");
+ writer.print(getTypeName(type));
+ writer.print("</td><td class=\"description\">");
+ writer.print(type.getAnnotation(ManagedObject.class).description());
+ writer.println("</td></tr>");
+
+ }
+ writer.println("</tbody>");
+ }
+
+ writer.println("</table>");
+ }
+
+ private String getTypeName(final Class<? extends ConfiguredObject> type)
+ {
+ return type.getAnnotation(ManagedObject.class).type() == null
+ ? _model.getTypeRegistry().getTypeClass(type).getSimpleName()
+ : type.getAnnotation(ManagedObject.class).type();
+ }
+
+ private void writeAttributes(final PrintWriter writer)
+ {
+ writer.println("<a name=\"types\"><h2>Attributes</h2></a>");
+ writer.println("<h2>Common Attributes</h2>");
+
+ writeAttributesTable(writer, _model.getTypeRegistry().getAttributeTypes(getConfiguredClass()).values());
+
+ for(Class<? extends ConfiguredObject> type : _types)
+ {
+
+ ManagedObject typeAnnotation = type.getAnnotation(ManagedObject.class);
+ String typeName = typeAnnotation.type() == null ? _model.getTypeRegistry().getTypeClass(type).getSimpleName() : typeAnnotation.type();
+ Collection<ConfiguredObjectAttribute<?, ?>> typeSpecificAttributes =
+ _model.getTypeRegistry().getTypeSpecificAttributes(type);
+ if(!typeSpecificAttributes.isEmpty())
+ {
+ writer.println("<h2><span class=\"type\">"+typeName+"</span> Specific Attributes</h2>");
+ writeAttributesTable(writer, typeSpecificAttributes);
+ }
+
+
+ }
+
+ }
+
+ private void writeAttributesTable(final PrintWriter writer,
+ final Collection<ConfiguredObjectAttribute<?, ?>> attributeTypes)
+ {
+ writer.println("<table class=\"attributes\">");
+ writer.println("<thead>");
+ writer.println("<tr><th class=\"name\">Attribute Name</th><th class=\"type\">Type</th><th class=\"description\">Description</th></tr>");
+ writer.println("</thead>");
+ writer.println("<tbody>");
+
+ for(ConfiguredObjectAttribute attribute : attributeTypes)
+ {
+ if(!attribute.isDerived())
+ {
+ writer.println("<tr><td class=\"name\">"
+ + attribute.getName()
+ + "</td><td class=\"type\">"
+ + renderType(attribute)
+ + "</td class=\"description\"><td>"
+ + attribute.getDescription()
+ + "</td></tr>");
+ }
+ }
+ writer.println("</tbody>");
+
+ writer.println("</table>");
+
+ }
+
+ private String renderType(final ConfiguredObjectAttribute attribute)
+ {
+ final Class type = attribute.getType();
+ if(Number.class.isAssignableFrom(type))
+ {
+ return "number";
+ }
+ else if(Enum.class.isAssignableFrom(type))
+ {
+ return "<span title=\"enum: " + EnumSet.allOf(type) + "\">string</span>";
+ }
+ else if(Boolean.class == type)
+ {
+ return "boolean";
+ }
+ else if(String.class == type)
+ {
+ return "string";
+ }
+ else if(UUID.class == type)
+ {
+ return "<span title=\"\">string</span>";
+ }
+ else if(List.class.isAssignableFrom(type))
+ {
+ // TODO - generate a description of the type in the array
+ return "array";
+ }
+ else if(Map.class.isAssignableFrom(type))
+ {
+ // TODO - generate a description of the type in the object
+ return "object";
+ }
+ else if(ConfiguredObject.class.isAssignableFrom(type))
+ {
+ return "<span title=\"name or id of a" + (VOWELS.contains(type.getSimpleName().toLowerCase().charAt(0)) ? "n " : " ") + type.getSimpleName() + "\">string</span>";
+ }
+ return type.getSimpleName();
+ }
+
+ private void writeFoot(final PrintWriter writer)
+ {
+ writer.println("</body>");
+ writer.println("</html>");
+ }
+ private Class<? extends ConfiguredObject> getConfiguredClass()
+ {
+ return _hierarchy.length == 0 ? Broker.class : _hierarchy[_hierarchy.length-1];
+ }
+
+
+ private int getIntParameterFromRequest(final HttpServletRequest request,
+ final String paramName,
+ final int defaultValue)
+ {
+ int intValue = defaultValue;
+ final String stringValue = request.getParameter(paramName);
+ if(stringValue!=null)
+ {
+ try
+ {
+ intValue = Integer.parseInt(stringValue);
+ }
+ catch (NumberFormatException e)
+ {
+ LOGGER.warn("Could not parse " + stringValue + " as integer for parameter " + paramName);
+ }
+ }
+ return intValue;
+ }
+
+ private boolean getBooleanParameterFromRequest(HttpServletRequest request, final String paramName)
+ {
+ return Boolean.parseBoolean(request.getParameter(paramName));
+ }
+
+
+}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MetaDataServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MetaDataServlet.java
index b995bb442c..88705563af 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MetaDataServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MetaDataServlet.java
@@ -51,14 +51,15 @@ public class MetaDataServlet extends AbstractServlet
private Model _instance;
+ public MetaDataServlet(final Model model)
+ {
+ _instance = model;
+ }
+
@Override
public void init() throws ServletException
{
super.init();
-
- _instance = BrokerModel.getInstance();
-
-
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/apidocs.css b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/apidocs.css
new file mode 100644
index 0000000000..bb1bf292d3
--- /dev/null
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/apidocs.css
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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.
+ *
+ */
+html, body {
+ margin-top: 20px;
+ margin-left: 40px;
+ margin-right: 40px;
+ padding: 0;
+ font-family: Lucida Sans,Lucida Grande,Arial,sans-serif !important;
+ font-size: 13px !important;
+ background: white;
+ color: #333;
+}
+
+th, td {
+ text-align: left;
+}
+
+.type {
+ font-family: "Courier New", courier, monospace;
+}
+
+table.types td.name {
+ width: 20em;
+}
+
+table.attributes td.name {
+ font-family: "Courier New", courier, monospace;
+ width: 20em;
+}
+
+
+table.attributes td.type {
+ width: 7em;
+}