org.jitsi.rest.AbstractJSONHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.jitsi.rest.AbstractJSONHandler.java

Source

/*
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * 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.jitsi.rest;

import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
import net.java.sip.communicator.util.*;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.*;
import org.jitsi.service.version.*;
import org.jivesoftware.smack.packet.*;
import org.json.simple.*;
import org.osgi.framework.*;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

/**
 * Implements an abstract Jetty {@code Handler} which provides content in JSON
 * format.
 *
 * @author Lyubomir Marinov
 * @author Pawel Domas
 */
public abstract class AbstractJSONHandler extends AbstractHandler {
    /**
     * The default suffix/extension of the HTTP resources which provide access
     * to JSON representations.
     */
    private static final String DEFAULT_JSON_TARGET = null;

    /**
     * The HTTP GET method.
     */
    protected static final String GET_HTTP_METHOD = "GET";

    /**
     * The HTTP resource which checks the health of the server/service exposed
     * through {@code AbstractJSONHandler}. Explicitly defined as an attempt to
     * encourage consistency among the extenders.
     */
    private static final String HEALTH_TARGET = "/about/health";

    /**
     * The HTTP PATCH method.
     */
    protected static final String PATCH_HTTP_METHOD = "PATCH";

    /**
     * The HTTP POST method.
     */
    protected static final String POST_HTTP_METHOD = "POST";

    /**
     * The HTTP resource which returns the JSON representation of the
     * {@code Version} of the server/service exposed through
     * {@code AbstractJSONHandler}. Explicitly defined as an attempt to
     * encourage consistency among the extenders.
     */
    private static final String VERSION_TARGET = "/about/version";

    /**
     * Analyzes response IQ returned by {@code AbstractJSONHandler#handle()}
     * method(s) and translates XMPP error into HTTP status code.
     *
     * @param responseIQ the IQ that is not {@link ColibriConferenceIQ} from
     *        which XMPP error will be extracted.
     * @return HTTP status code
     */
    protected static int getHttpStatusCodeForResultIq(IQ responseIQ) {
        String condition = responseIQ.getError().getCondition();

        if (XMPPError.Condition.not_authorized.toString().equals(condition)) {
            return HttpServletResponse.SC_UNAUTHORIZED;
        } else if (XMPPError.Condition.service_unavailable.toString().equals(condition)) {
            return HttpServletResponse.SC_SERVICE_UNAVAILABLE;
        } else {
            return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
        }
    }

    /**
     * The {@code BundleContext} within which this instance is initialized.
     */
    protected final BundleContext bundleContext;

    /**
     * The suffix/extension of the HTTP resources which provide access to JSON
     * representations.
     */
    private final String jsonTarget;

    /**
     * Initializes a new {@code AbstractJSONHandler} instance within a specific
     * {@code BundleContext}.
     *
     * @param bundleContext the {@code BundleContext} within which the new
     * instance is to be initialized
     */
    protected AbstractJSONHandler(BundleContext bundleContext) {
        this.bundleContext = bundleContext;

        // jsonTarget
        String jsonTarget = DEFAULT_JSON_TARGET;

        if (jsonTarget != null && !jsonTarget.startsWith("."))
            jsonTarget = "." + jsonTarget;
        this.jsonTarget = jsonTarget;
    }

    /**
     * Begins an {@link HttpServletResponse} the handling of which appears to
     * have chances of success.
     *
     * @param target the target of the request
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     */
    protected void beginResponse(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) {
        beginResponse(target, baseRequest, request, response, RESTUtil.JSON_CONTENT_TYPE_WITH_CHARSET);
    }

    /**
     * Begins an {@link HttpServletResponse} the handling of which appears to
     * have chances of success.
     *
     * @param target the target of the request
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     * @param contentType the MIME type of the content to be set on
     * {@code response}
     */
    protected void beginResponse(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response, String contentType) {
        response.setContentType(contentType);
        // Cross-origin resource sharing (CORS)
        response.setHeader("Access-Control-Allow-Origin", "*");
    }

    /**
     * Gets a JSON representation of the health (status) of the associated
     * server/service. The default implementation does nothing because it serves
     * as a placeholder for extenders.
     *
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     * @throws IOException
     * @throws ServletException
     */
    protected void doGetHealthJSON(Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    }

    /**
     * Gets a JSON representation of the {@code Version} of the associated
     * server/service.
     *
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     * @throws IOException
     * @throws ServletException
     */
    protected void doGetVersionJSON(Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        beginResponse(/*target*/ null, baseRequest, request, response);

        BundleContext bundleContext = getBundleContext();
        int status = HttpServletResponse.SC_SERVICE_UNAVAILABLE;

        if (bundleContext != null) {
            VersionService versionService = ServiceUtils.getService(bundleContext, VersionService.class);

            if (versionService != null) {
                org.jitsi.service.version.Version version = versionService.getCurrentVersion();
                JSONObject versionJSONObject = new JSONObject();

                versionJSONObject.put("name", version.getApplicationName());
                versionJSONObject.put("version", version.toString());
                versionJSONObject.put("os", System.getProperty("os.name"));

                Writer writer = response.getWriter();

                response.setStatus(status = HttpServletResponse.SC_OK);
                versionJSONObject.writeJSONString(writer);
            }
        }

        if (response.getStatus() != status)
            response.setStatus(status);

        endResponse(/*target*/ null, baseRequest, request, response);
    }

    /**
     * Ends an {@link HttpServletResponse}.
     *
     * @param target the target of the request
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     */
    protected void endResponse(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) {
        if (!baseRequest.isHandled()) {
            if (response.getStatus() == 0)
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            baseRequest.setHandled(true);
        }
    }

    /**
     * Gets the {@code BundleContext} in which this Jetty {@code Handler} has
     * been started.
     *
     * @return the {@code BundleContext} in which this Jetty {@code Handler}
     * has been started or {@code null} if this Jetty {@code Handler} has not
     * been started in a {@code BundleContext}
     */
    public BundleContext getBundleContext() {
        return bundleContext;
    }

    /**
     * Gets the OSGi service instance of a specific {@code Class} available to
     * this Jetty {@code Handler}.
     *
     * @param <T> the type of the OSGi service to retrieve
     * @param serviceClass the {@code Class} of the OSGi service to retrieve
     * @return the OSGi service instance of the specified {@code serviceClass}
     * available to this Jetty {@code Handler} or {@code null} if no such
     * service instance is available
     */
    public <T> T getService(Class<T> serviceClass) {
        BundleContext bundleContext = getBundleContext();
        T service = (bundleContext == null) ? null : ServiceUtils.getService(bundleContext, serviceClass);

        return service;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        if (target != null) {
            // The target ends with ".json".
            int jsonTargetLength = (jsonTarget == null) ? 0 : jsonTarget.length();

            if (jsonTargetLength == 0 || target.endsWith(jsonTarget)) {
                target = target.substring(0, target.length() - jsonTargetLength);

                handleJSON(target, baseRequest, request, response);
            }
        }
    }

    /**
     * Handles an HTTP request for a {@link #HEALTH_TARGET}-related resource.
     *
     * @param target the target of the request
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     * @throws IOException
     * @throws ServletException
     */
    protected void handleHealthJSON(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException, ServletException {
        if (GET_HTTP_METHOD.equals(request.getMethod())) {
            // Check/get the health (status) of the associated server/service.
            doGetHealthJSON(baseRequest, request, response);
        } else {
            response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * Handles a specific HTTP request for JSON content.
     *
     * @param target
     * @param baseRequest
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException 
     */
    protected void handleJSON(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException, ServletException {
        if (HEALTH_TARGET.equals(target)) {
            target = target.substring(HEALTH_TARGET.length());

            handleHealthJSON(target, baseRequest, request, response);
        } else if (VERSION_TARGET.equals(target)) {
            target = target.substring(VERSION_TARGET.length());

            handleVersionJSON(target, baseRequest, request, response);
        }
    }

    /**
     * Handles an HTTP request for a {@link #VERSION_TARGET}-related resource.
     *
     * @param target the target of the request
     * @param baseRequest the original unwrapped {@link Request} object
     * @param request the request either as the {@code Request} object or a
     * wrapper of that request
     * @param response the response either as the {@code Response} object or a
     * wrapper of that response
     * @throws IOException
     * @throws ServletException
     */
    protected void handleVersionJSON(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException, ServletException {
        if (GET_HTTP_METHOD.equals(request.getMethod())) {
            // Get the Version of the associated server/service.
            doGetVersionJSON(baseRequest, request, response);
        } else {
            response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }
}