org.sprintapi.api.http.servlet.HttpApiServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.sprintapi.api.http.servlet.HttpApiServlet.java

Source

/*
 *  Copyright 2012 the original author or authors.
 *
 *  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.sprintapi.api.http.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.CopyUtils;
import org.sprintapi.api.Context;
import org.sprintapi.api.error.ApiErrorException;
import org.sprintapi.api.http.HttpContext;
import org.sprintapi.api.http.HttpRequest;
import org.sprintapi.api.http.header.adapter.HttpDateAdapter;
import org.sprintapi.api.http.header.adapter.HttpHeaderAdapter;
import org.sprintapi.api.http.header.adapter.HttpLocationAdapter;
import org.sprintapi.api.http.header.adapter.accept.HttpAcceptAdapter;
import org.sprintapi.api.http.header.adapter.accept.HttpAcceptCharsetAdapter;
import org.sprintapi.api.http.header.adapter.accept.HttpAcceptLanguageAdapter;
import org.sprintapi.api.http.header.adapter.accept.HttpAllowAdapter;
import org.sprintapi.api.http.header.adapter.lang.HttpContentLanguageAdapter;
import org.sprintapi.api.http.header.adapter.media.HttpMediaTypeAdapter;
import org.sprintapi.api.http.method.HttpMethod;
import org.sprintapi.api.http.uri.HttpRequestUrl;
import org.sprintapi.api.impl.ResponseHolder;
import org.sprintapi.api.meta.ContentMetadata;
import org.sprintapi.api.meta.Metadata;
import org.sprintapi.api.meta.RequestMetadata;
import org.sprintapi.api.meta.ResponseMetadata;
import org.sprintapi.api.meta.impl.ResponseMetadataImpl;
import org.sprintapi.api.request.Request;
import org.sprintapi.api.response.Response;
import org.sprintapi.api.response.StatusCode;
import org.sprintapi.api.service.ApiService;

public abstract class HttpApiServlet extends GenericServlet {

    private static final long serialVersionUID = 5919203442935383094L;

    protected static final String POWERED_BY_VALUE = "API-Machine/1.0.0";

    private Map<String, HttpMethod> methods;

    private Map<String, HttpHeaderAdapter<?>> requestMetaAdapters;
    private Map<String, HttpHeaderAdapter<?>> responseMetaAdapters;
    private Map<String, HttpHeaderAdapter<?>> contentMetaAdapters;

    private HttpServletConfig config;

    public HttpApiServlet() {
        super();
        this.methods = new HashMap<String, HttpMethod>();
        this.requestMetaAdapters = new HashMap<String, HttpHeaderAdapter<?>>();
        this.responseMetaAdapters = new HashMap<String, HttpHeaderAdapter<?>>();
        this.contentMetaAdapters = new HashMap<String, HttpHeaderAdapter<?>>();
    }

    protected abstract ApiService<?, ?> getHttpDispatcher();

    protected abstract ApiService<?, ?> getContextDispatcher();

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        this.config = new HttpServletConfig(servletConfig);

        setHttpMethod(HttpMethod.GET);
        setHttpMethod(HttpMethod.DELETE);
        setHttpMethod(HttpMethod.HEAD);
        setHttpMethod(HttpMethod.OPTIONS);
        setHttpMethod(HttpMethod.POST);
        setHttpMethod(HttpMethod.PUT);
        setHttpMethod(HttpMethod.TRACE);

        // request headers
        setRequestHeaderAdapter(RequestMetadata.ACCEPT_NAME, new HttpAcceptAdapter(InputStream.class));
        setRequestHeaderAdapter(RequestMetadata.ACCEPT_LANGUAGE_NAME, new HttpAcceptLanguageAdapter());
        setRequestHeaderAdapter(RequestMetadata.ACCEPT_CHARSET_NAME, new HttpAcceptCharsetAdapter());
        setRequestHeaderAdapter(RequestMetadata.DATE_NAME, new HttpDateAdapter());

        // response headers
        setResponseHeaderAdapter(ResponseMetadata.DATE_NAME, new HttpDateAdapter());
        setResponseHeaderAdapter(ResponseMetadata.LOCATION_NAME, new HttpLocationAdapter());

        // entity headers
        setContentHeaderAdapter(ContentMetadata.ALLOW_NAME, new HttpAllowAdapter());
        setContentHeaderAdapter(ContentMetadata.CONTENT_TYPE_NAME, new HttpMediaTypeAdapter());
        setContentHeaderAdapter(ContentMetadata.CONTENT_LANGUAGE_NAME, new HttpContentLanguageAdapter());
    }

    protected void setHttpMethod(HttpMethod method) {
        methods.put(method.name(), method);
    }

    protected void setRequestHeaderAdapter(final String name, final HttpHeaderAdapter<?> adapter) {
        requestMetaAdapters.put(name.toLowerCase(Locale.US), adapter);
    }

    protected void setContentHeaderAdapter(final String name, final HttpHeaderAdapter<?> adapter) {
        contentMetaAdapters.put(name.toLowerCase(Locale.US), adapter);
    }

    protected void setResponseHeaderAdapter(final String name, final HttpHeaderAdapter<?> adapter) {
        responseMetaAdapters.put(name.toLowerCase(Locale.US), adapter);
    }

    protected void service(Request request, Response response, Context context)
            throws ApiErrorException, IOException {
        getHttpDispatcher().dispatch(request, response, context);
    }

    protected void doService(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
            throws ApiErrorException, IOException {

        // Append signature
        if (config.isPoweredByAllowed()) {
            httpResponse.setHeader(Metadata.X_POWERED_BY_NAME, POWERED_BY_VALUE);
        }

        if (httpRequest.getMethod() == null) {
            throw new ApiErrorException(StatusCode.BAD_REQUEST,
                    "The request is malformed. Requested method is missing.");
        }

        if (!methods.containsKey(httpRequest.getMethod().toUpperCase())) {
            throw new ApiErrorException(StatusCode.NOT_IMPLEMENTED,
                    "The requested method '" + httpRequest.getMethod() + "' is not supported.");
        }

        // Wrap request
        final HttpRequest request = new HttpRequest(httpRequest, methods.get(httpRequest.getMethod().toUpperCase()),
                requestMetaAdapters, contentMetaAdapters);

        // Server options request? (OPTIONS *) 
        if (request.getUri().isAsterisk()) {
            if (HttpMethod.OPTIONS.name().equalsIgnoreCase(httpRequest.getMethod())) {
                throw new ApiErrorException(StatusCode.NOT_IMPLEMENTED, "Not Implemented.");
            }
            throw new ApiErrorException(StatusCode.BAD_REQUEST, "The request is malformed.");
        }

        // Create response
        ResponseHolder<?> response = new ResponseHolder<Object>();
        response.setMetadata(new ResponseMetadataImpl());

        HttpContext<?, ?> context = HttpContext.getInstance(httpRequest);
        context.setDispatcher(getContextDispatcher());

        service((Request) request, (Response) response, (Context) context);

        if (response.getStatus() == null) {
            response.setStatus(StatusCode.OK);
        }

        // Set response status
        httpResponse.setStatus((response.getStatus() != null) ? response.getStatus().getCode() : 0);

        // Write response headers
        if (response.getMetadata() != null) {
            writeHeaders(responseMetaAdapters, httpResponse, request, response.getMetadata(), context);
        }

        // Finish if there is no content to write
        if (response.getContent() == null) {
            return;
        }

        // Write content headers
        if (response.getContent().getMetadata() != null) {
            writeHeaders(contentMetaAdapters, httpResponse, request, response.getContent().getMetadata(), context);
        }

        // Write response content length      
        //      if (context.getResponseContext().getContentType() != null) {
        //         httpResponse.setContentType(context.getResponseContext().getContentType().toString());
        //      }

        // Write response content body
        if (response.getContent().getBody() != null) {
            CopyUtils.copy((InputStream) response.getContent().getBody(), httpResponse.getOutputStream());
        }
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse)
            throws ServletException, IOException {

        HttpServletRequest nativeHttpRequest = null;
        HttpServletResponse nativeHttpResponse = null;

        try {
            nativeHttpRequest = (HttpServletRequest) servletRequest;
            nativeHttpResponse = (HttpServletResponse) servletResponse;

        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }

        if ((nativeHttpRequest == null) || (nativeHttpResponse == null)) {
            throw new ServletException("non-HTTP request or response");
        }

        try {
            doService(nativeHttpRequest, nativeHttpResponse);

        } catch (ApiErrorException e) {
            handleError(e, nativeHttpResponse);

        } catch (Exception e) {
            handleError(new ApiErrorException(HttpRequestUrl.getInstance(nativeHttpRequest),
                    StatusCode.INTERNAL_SERVER_ERROR, e), nativeHttpResponse);
        }
    }

    protected void handleError(ApiErrorException ex, HttpServletResponse httpResponse) {
        httpResponse.setStatus(ex.getStatusCode().getCode());
        httpResponse.setContentType("text/plain; charset=utf-8");
        httpResponse.setHeader(ContentMetadata.CONTENT_LANGUAGE_NAME, "en");

        try {
            httpResponse.getWriter().append(ex.getMessage()).close();
        } catch (IOException e) {
            log(ex.getMessage(), ex.getCause());
        }
        if (StatusCode.Category.ServerError.equals(ex.getStatusCode().getCategory())) {
            log(ex.getMessage(), ex.getCause());
        }
    }

    protected static void writeHeaders(Map<String, HttpHeaderAdapter<?>> adapters, HttpServletResponse httpResponse,
            Request request, Metadata metadata, Context context) throws ApiErrorException {
        if ((metadata == null) || (metadata.names() == null)) {
            return;
        }
        for (String meta : metadata.names()) {
            Object value = metadata.get(meta);
            if (value != null) {
                String httpValue = value.toString();
                HttpHeaderAdapter metaAdapter = (HttpHeaderAdapter<?>) (adapters.containsKey(
                        meta.toLowerCase(Locale.US)) ? adapters.get(meta.toLowerCase(Locale.US)) : null);
                if (metaAdapter != null) {
                    httpValue = metaAdapter.write(value);
                }
                httpResponse.setHeader(meta, httpValue);
            }
        }
    }
}