All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencastproject.runtimeinfo.RestDocsServlet Maven / Gradle / Ivy

There is a newer version: 16.7
Show newest version
/**
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community 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://opensource.org/licenses/ecl2.txt
 *
 * 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.opencastproject.runtimeinfo;

import static org.opencastproject.rest.RestConstants.SERVICES_FILTER;
import static org.opencastproject.rest.RestConstants.SERVICE_PATH_PROPERTY;
import static org.opencastproject.util.data.Option.none;
import static org.opencastproject.util.data.Option.some;

import org.opencastproject.runtimeinfo.rest.RestDocData;
import org.opencastproject.systems.OpencastConstants;
import org.opencastproject.util.data.Option;
import org.opencastproject.util.doc.DocUtil;
import org.opencastproject.util.doc.rest.RestQuery;
import org.opencastproject.util.doc.rest.RestService;

import org.apache.commons.lang3.StringUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardContextSelect;
import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletName;
import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletPattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

/** A bundle activator that registers the REST documentation servlet. */
@Component(service = Servlet.class)
@HttpWhiteboardServletName(RestDocsServlet.SERVLET_PATH)
@HttpWhiteboardServletPattern(RestDocsServlet.SERVLET_PATH)
@HttpWhiteboardContextSelect("(osgi.http.whiteboard.context.name=opencast)")
public class RestDocsServlet extends HttpServlet {

  /** The logger */
  private static final Logger logger = LoggerFactory.getLogger(RestDocsServlet.class);

  /** The servlet path */
  public static final String SERVLET_PATH = "/docs.html";

  /** The query string parameter used to specify a specific service */
  private static final String PATH_PARAM = "path";

  /** java.io serialization UID */
  private static final long serialVersionUID = 6930336096831297329L;

  /** The OSGI bundle context */
  protected BundleContext bundleContext;

  /** A map of global macro values for REST documentation. */
  private Map globalMacro;

  @Activate
  public void start(BundleContext bundleContext) throws Exception {
    this.bundleContext = bundleContext;
    prepareMacros();
  }

  /** Add a list of global information, such as the server URL, to the globalMacro map. */
  private void prepareMacros() {
    globalMacro = new HashMap();
    globalMacro.put("PING_BACK_URL", bundleContext.getProperty("org.opencastproject.anonymous.feedback.url"));
    globalMacro.put("HOST_URL", bundleContext.getProperty(OpencastConstants.SERVER_URL_PROPERTY));
    globalMacro.put("LOCAL_STORAGE_DIRECTORY", bundleContext.getProperty("org.opencastproject.storage.dir"));
  }

  public void stop(BundleContext bundleContext) throws Exception {
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String docPath = req.getParameter(PATH_PARAM);
    if (StringUtils.isBlank(docPath)) {
      resp.sendRedirect("rest_docs.html");
    } else {
      // write the details for this service
      writeServiceDocumentation(docPath, resp);
    }
  }

  private void writeServiceDocumentation(final String docPath, HttpServletResponse resp)
          throws IOException {
    ServiceReference reference = null;
    for (ServiceReference ref : getRestEndpointServices()) {
      String alias = (String) ref.getProperty(SERVICE_PATH_PROPERTY);
      if (docPath.equalsIgnoreCase(alias)) {
        reference = ref;
        break;
      }
    }

    final StringBuilder docs = new StringBuilder();

    if (reference == null) {
      docs.append("REST docs unavailable for ");
      docs.append(docPath);
    } else {
      final Object restService = bundleContext.getService(reference);
      findRestAnnotation(restService.getClass()).fold(new Option.Match() {
        @Override
        public Void some(RestService annotation) {
          globalMacro.put("SERVICE_CLASS_SIMPLE_NAME", restService.getClass().getSimpleName());
          RestDocData data = new RestDocData(annotation.name(), annotation.title(), docPath, annotation.notes());
          data.setAbstract(annotation.abstractText());

          Produces producesClass = (Produces) restService.getClass().getAnnotation(Produces.class);

          for (Method m : restService.getClass().getMethods()) {
            RestQuery rq = (RestQuery) m.getAnnotation(RestQuery.class);
            String httpMethodString = null;
            for (Annotation a : m.getAnnotations()) {
              HttpMethod httpMethod = (HttpMethod) a.annotationType().getAnnotation(HttpMethod.class);
              if (httpMethod != null) {
                httpMethodString = httpMethod.value();
              }
            }
            Produces produces = (Produces) m.getAnnotation(Produces.class);
            if (produces == null) {
              produces = producesClass;
            }
            Path path = (Path) m.getAnnotation(Path.class);
            Class returnType = m.getReturnType();
            if ((rq != null) && (httpMethodString != null) && (path != null)) {
              data.addEndpoint(rq, returnType, produces, httpMethodString, path);
            }
          }
          String template = DocUtil.loadTemplate("/ui/restdocs/template.xhtml");
          docs.append(DocUtil.generate(data, template));
          return null;
        }

        @Override
        public Void none() {
          docs.append("No documentation has been found for ").append(restService.getClass().getSimpleName());
          return null;
        }
      });
    }

    resp.setContentType("text/html");
    resp.getWriter().write(docs.toString());
  }

  private ServiceReference[] getRestEndpointServices() {
    try {
      return bundleContext.getAllServiceReferences(null, SERVICES_FILTER);
    } catch (InvalidSyntaxException e) {
      logger.warn("Unable to query the OSGI service registry for all registered rest endpoints");
      return new ServiceReference[0];
    }
  }

  /** Try to find the RestService annotation starting at endpointClass. */
  public static Option findRestAnnotation(Class endpointClass) {
    if (endpointClass == null) {
      return none();
    }
    final RestService rs = endpointClass.getAnnotation(RestService.class);
    if (rs == null) {
      return findRestAnnotation(endpointClass.getSuperclass());
    } else {
      return some(rs);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy