Package org.apache.droids.localserver

Source Code of org.apache.droids.localserver.LocalHttpServer$RequestListener$Worker

/*
* 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.
*/
package org.apache.droids.localserver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.common.base.Preconditions;

import org.apache.http.HttpException;
import org.apache.http.HttpServerConnection;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Local HTTP server for tests that require one.
*/
public class LocalHttpServer
{

  private final Logger log = LoggerFactory.getLogger(LocalHttpServer.class);
  /**
   * The local address to bind to. The host is an IP number rather than
   * "localhost" to avoid surprises on hosts that map "localhost" to an IPv6
   * address or something else. The port is 0 to let the system pick one.
   */
  public final static InetSocketAddress TEST_SERVER_ADDR = new InetSocketAddress("127.0.0.1", 0);
  /** The request handler registry. */
  private final HttpRequestHandlerRegistry handlerRegistry;
  /**
   * The HTTP processor. If the interceptors are thread safe and the list is not
   * modified during operation, the processor is thread safe.
   */
  private final BasicHttpProcessor httpProcessor;
  /** The server parameters. */
  private final HttpParams params;
  /** The server socket, while being served. */
  private volatile ServerSocket servicedSocket;
  /** The request listening thread, while listening. */
  private volatile Thread listenerThread;
  /** The number of connections this accepted. */
  private final AtomicInteger acceptedConnections = new AtomicInteger(0);

  /**
   * Creates a new test server.
   */
  public LocalHttpServer()
  {
    this.handlerRegistry = new HttpRequestHandlerRegistry();
    this.httpProcessor = new BasicHttpProcessor();
    this.httpProcessor.addInterceptor(new ResponseDate());
    this.httpProcessor.addInterceptor(new ResponseServer());
    this.httpProcessor.addInterceptor(new ResponseContent());
    this.httpProcessor.addInterceptor(new ResponseConnControl());
    this.params = new BasicHttpParams();
    this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024).setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true).setParameter(CoreProtocolPNames.ORIGIN_SERVER, "LocalTestServer/1.1");
  }

  /**
   * Returns the number of connections this test server has accepted.
   */
  public int getAcceptedConnectionCount()
  {
    return this.acceptedConnections.get();
  }

  /**
   * Registers a handler with the local registry.
   *
   * @param pattern
   *          the URL pattern to match
   * @param handler
   *          the handler to apply
   */
  public void register(String pattern, HttpRequestHandler handler)
  {
    this.handlerRegistry.register(pattern, handler);
  }

  /**
   * Unregisters a handler from the local registry.
   *
   * @param pattern
   *          the URL pattern
   */
  public void unregister(String pattern)
  {
    this.handlerRegistry.unregister(pattern);
  }

  /**
   * Starts this test server. Use {@link #getServicePort getServicePort} to
   * obtain the port number afterwards.
   */
  public void start() throws IOException
  {
    if (servicedSocket != null) {
      return; // Already running
    }

    ServerSocket ssock = new ServerSocket();
    ssock.setReuseAddress(true); // probably pointless for port '0'
    ssock.bind(TEST_SERVER_ADDR);
    this.servicedSocket = ssock;

    this.listenerThread = new Thread(new RequestListener());
    this.listenerThread.setDaemon(false);
    this.listenerThread.start();
  }

  /**
   * Stops this test server.
   */
  public void stop() throws IOException
  {
    if (this.servicedSocket == null) {
      return; // not running
    }

    try {
      this.servicedSocket.close();
    } catch (IOException ex) {
      log.error(ex.getMessage(), ex);
    } finally {
      this.servicedSocket = null;
    }

    if (this.listenerThread != null) {
      this.listenerThread.interrupt();
      this.listenerThread = null;
    }
  }

  @Override
  public String toString()
  {
    ServerSocket ssock = servicedSocket; // avoid synchronization
    StringBuffer sb = new StringBuffer(80);
    sb.append("LocalTestServer/");
    if (ssock == null) {
      sb.append("stopped");
    } else {
      sb.append(ssock.getLocalSocketAddress());
    }
    return sb.toString();
  }

  /**
   * Obtains the port this server is servicing.
   *
   * @return the service port
   */
  public int getServicePort()
  {
    ServerSocket ssock = this.servicedSocket; // avoid synchronization
    Preconditions.checkState(ssock != null, "not running");
    return ssock.getLocalPort();
  }

  /**
   * Obtains the local address the server is listening on
   *
   * @return the service address
   */
  public SocketAddress getServiceAddress()
  {
    ServerSocket ssock = this.servicedSocket; // avoid synchronization
    Preconditions.checkState(ssock != null, "not running");
    return ssock.getLocalSocketAddress();
  }

  /**
   * The request listener. Accepts incoming connections and launches a service
   * thread.
   */
  public class RequestListener implements Runnable
  {

    /** The workers launched from here. */
    private final Set<Thread> workerThreads;

    public RequestListener()
    {
      super();
      this.workerThreads = Collections.synchronizedSet(new HashSet<Thread>());
    }

    public void run()
    {
      try {
        while ((servicedSocket != null) && (listenerThread == Thread.currentThread())
                && !Thread.interrupted()) {
          try {
            accept();
          } catch (Exception ex) {
            ServerSocket ssock = servicedSocket;
            if ((ssock != null) && !ssock.isClosed()) {
              log.error(LocalHttpServer.this.toString() + " could not accept", ex);
            }
            // otherwise ignore the exception silently
            break;
          }
        }
      } finally {
        cleanup();
      }
    }

    protected void accept() throws IOException
    {
      // Set up HTTP connection
      Socket socket = servicedSocket.accept();
      acceptedConnections.incrementAndGet();
      DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
      conn.bind(socket, params);

      // Set up the HTTP service
      HttpService httpService = new HttpService(httpProcessor,
              new DefaultConnectionReuseStrategy(), new DefaultHttpResponseFactory());
      httpService.setParams(params);
      httpService.setHandlerResolver(handlerRegistry);

      // Start worker thread
      Thread t = new Thread(new Worker(httpService, conn));
      workerThreads.add(t);
      t.setDaemon(true);
      t.start();

    }

    protected void cleanup()
    {
      Thread[] threads = workerThreads.toArray(new Thread[0]);
      for (int i = 0; i < threads.length; i++) {
        if (threads[i] != null) {
          threads[i].interrupt();
        }
      }
    }

    /**
     * A worker for serving incoming requests.
     */
    public class Worker implements Runnable
    {

      private final HttpService httpservice;
      private final HttpServerConnection conn;

      public Worker(final HttpService httpservice, final HttpServerConnection conn)
      {

        this.httpservice = httpservice;
        this.conn = conn;
      }

      public void run()
      {
        HttpContext context = new BasicHttpContext(null);
        try {
          while ((servicedSocket != null) && this.conn.isOpen() && !Thread.interrupted()) {
            this.httpservice.handleRequest(this.conn, context);
          }
        } catch (IOException ex) {
          // ignore silently
        } catch (HttpException ex) {
          // ignore silently
        } finally {
          workerThreads.remove(Thread.currentThread());
          try {
            this.conn.shutdown();
          } catch (IOException ignore) {
          }
        }
      }
    }
  }
}
TOP

Related Classes of org.apache.droids.localserver.LocalHttpServer$RequestListener$Worker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.