com.ctriposs.rest4j.server.Rest4JServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.ctriposs.rest4j.server.Rest4JServlet.java

Source

/**
 * Copyright (C) 2014 the original author or authors.
 * See the notice.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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 com.ctriposs.rest4j.server;

import com.linkedin.parseq.Engine;
import com.linkedin.parseq.EngineBuilder;
import com.ctriposs.r2.transport.http.server.AbstractR2Servlet;
import com.ctriposs.r2.transport.http.server.AsyncR2Servlet;
import com.ctriposs.rest4j.server.resources.PrototypeResourceFactory;
import com.ctriposs.rest4j.server.resources.ResourceFactory;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;

import com.ctriposs.r2.transport.http.server.RAPServlet;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Rest4J HttpServlet implementation.
 * 
 * <p>Resource packages are provided as a servlet config init parameter in the web.xml.  If
 * providing multiple resource packages, separate them by with the ';' character.</p>
 * 
 * <pre><code>
 * <web-app>
 * ...
 *  <servlet>
 *    <servlet-class>com.ctriposs.rest4j.server.Rest4JServlet</servlet-class>
 *      <init-param>
 *        <param-name>resourcePackages</param-name>
 *        <param-value>com.example.package.one;com.example.package.two</param-value>
 *      </init-param>
 *      ...
 * </code></pre>
 *
 * <p>Async request handling is available for Servlet API 3.0 or greater and enabled by default for 3.0+.</p>
 *
 * <p>To use async in Servlet containers, async-supported must be set to true in your web.xml, e.g.:</p>
 *
 * <pre>
 * <code>
 *   <servlet>
 *     ...
 *     <async-supported>true</async-supported>
 *     ...
 *   </servlet>
 * </code>
 * </pre>
 *
 * <p>Async can be further configured by the rest4j uscAsync servlet param (which defaults to true for servlet API 3.0+
 * servlet containers).  And the asyncTimeout param can be set to a desired maximum timeout value in milliseconds, e.g.:</p>
 *
 * <pre><code>
 *   <init-param>
 *     <param-name>useAysnc</param-name>
 *     <param-value>true</param-value>
 *   </init-param>
 *   <init-param>
 *     <param-name>asyncTimeout</param-name>
 *     <param-value>30000</param-value>
 *   </init-param>
 * </code></pre>
 *
 * <p>A parseqThreadPoolSize param may also be optionally set using the same init-param style.  If absent,
 * the size defaults to the processor core count plus one.  This setting controls the thread pool used
 * for parseq outbound requests ONLY.</p>
 *
 * <p>To set the number of server threads please refer to the documentation for the particular servlet container
 * you are using.</p>
 *
 * <p>JSR-330 Dependency Injection (@Named, @Inject) is not available on Resource classes when using this servlet.
 * If dependency injection is needed, please see rest4j documentation about available integrations with dependency
 * injection frameworks (guice, spring...).</p>
 * 
 */

public class Rest4JServlet extends HttpServlet {
    private static final Logger log = LoggerFactory.getLogger(Rest4JServlet.class);

    private static final long serialVersionUID = 1L;
    private static final String RESOURCE_PACKAGES_PARAM = "resourcePackages";
    private static final String PAR_SEQ_THREAD_POOL_SIZE = "parSeqThreadPoolSize";
    private static final String PACKAGE_PARAM_SEPARATOR = ";";
    private static final String USE_ASYNC_PARAM = "useAsync";
    private static final String ASYNC_TIMEOUT_PARAM = "asyncTimeout";
    public static final int DEFAULT_ASYNC_TIMEOUT = 30000;

    private HttpServlet _r2Servlet;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);
        _r2Servlet = buildR2ServletFromServletParams(servletConfig);
        _r2Servlet.init(servletConfig);
    }

    private AbstractR2Servlet buildR2ServletFromServletParams(ServletConfig servletConfig) {
        ResourceFactory resourceFactory = new PrototypeResourceFactory();

        Rest4JConfig config = new Rest4JConfig();
        config.setResourcePackageNamesSet(getResourcePackageSet(servletConfig));

        final ScheduledExecutorService scheduler = Executors
                .newScheduledThreadPool(getParseqThreadPoolSize(servletConfig));
        Engine engine = new EngineBuilder().setTaskExecutor(scheduler).setTimerScheduler(scheduler).build();

        DelegatingTransportDispatcher dispatcher = new DelegatingTransportDispatcher(
                new Rest4JServer(config, resourceFactory, engine));

        boolean useAsync = getUseAsync(servletConfig);
        long asyncTimeOut = getAsyncTimeout(servletConfig);

        if (useAsync && servletConfig.getServletContext().getMajorVersion() < 3) {
            throw new IllegalArgumentException(
                    "This servlet is configured with useAsync=true, but the current servlet "
                            + "context does not support the required Servlet API 3.0.");

        }
        if (!useAsync) {
            log.info(
                    "Initializing Rest4J with a thread based request handling.  Set useAsync=true on a Servlet API 3.0 container to enable Rest4J's async servlet.");
            return new RAPServlet(dispatcher);
        } else {
            log.info("Initializing Rest4J with an async request handling enabled.");
            return new AsyncR2Servlet(dispatcher, asyncTimeOut);
        }
    }

    private boolean getUseAsync(ServletConfig servletConfig) {
        String useAsync = servletConfig.getInitParameter(USE_ASYNC_PARAM);
        if (useAsync != null) {
            return Boolean.parseBoolean(useAsync);
        } else {
            return servletConfig.getServletContext().getMajorVersion() >= 3;
        }
    }

    private long getAsyncTimeout(ServletConfig servletConfig) {
        String asyncTimeout = servletConfig.getInitParameter(ASYNC_TIMEOUT_PARAM);
        if (asyncTimeout != null) {
            return Long.parseLong(asyncTimeout);
        } else {
            return DEFAULT_ASYNC_TIMEOUT;
        }
    }

    private Set<String> getResourcePackageSet(ServletConfig servletConfig) {
        String resourcePackages = servletConfig.getInitParameter(RESOURCE_PACKAGES_PARAM);
        Set<String> resourcePackageSet = new HashSet<String>();
        for (String resourcePackage : resourcePackages.split(PACKAGE_PARAM_SEPARATOR)) {
            resourcePackageSet.add(resourcePackage.trim());
        }
        return resourcePackageSet;
    }

    private int getParseqThreadPoolSize(ServletConfig servletConfig) {
        int threadPoolSize;

        String threadPoolSizeStr = servletConfig.getInitParameter(PAR_SEQ_THREAD_POOL_SIZE);
        if (StringUtils.isNumeric(threadPoolSizeStr) && !threadPoolSizeStr.isEmpty()) {
            threadPoolSize = Integer.valueOf(threadPoolSizeStr);
        } else {
            final int numCores = Runtime.getRuntime().availableProcessors();
            threadPoolSize = numCores + 1;
        }
        return threadPoolSize;
    }

    @Override
    public void destroy() {
        _r2Servlet.destroy();
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        _r2Servlet.service(req, res);
    }
}