001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.impl.bootstrap;
028
029import java.io.IOException;
030import java.net.InetAddress;
031import java.net.ServerSocket;
032import java.util.Set;
033import java.util.concurrent.SynchronousQueue;
034import java.util.concurrent.ThreadPoolExecutor;
035import java.util.concurrent.TimeUnit;
036import java.util.concurrent.atomic.AtomicReference;
037
038import javax.net.ServerSocketFactory;
039import javax.net.ssl.SSLServerSocket;
040
041import org.apache.http.ExceptionLogger;
042import org.apache.http.HttpConnectionFactory;
043import org.apache.http.HttpServerConnection;
044import org.apache.http.config.SocketConfig;
045import org.apache.http.impl.DefaultBHttpServerConnection;
046import org.apache.http.protocol.HttpService;
047
048/**
049 * @since 4.4
050 */
051public class HttpServer {
052
053    enum Status { READY, ACTIVE, STOPPING }
054
055    private final int port;
056    private final InetAddress ifAddress;
057    private final SocketConfig socketConfig;
058    private final ServerSocketFactory serverSocketFactory;
059    private final HttpService httpService;
060    private final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory;
061    private final SSLServerSetupHandler sslSetupHandler;
062    private final ExceptionLogger exceptionLogger;
063    private final ThreadPoolExecutor listenerExecutorService;
064    private final ThreadGroup workerThreads;
065    private final WorkerPoolExecutor workerExecutorService;
066    private final AtomicReference<Status> status;
067
068    private volatile ServerSocket serverSocket;
069    private volatile RequestListener requestListener;
070
071    HttpServer(
072            final int port,
073            final InetAddress ifAddress,
074            final SocketConfig socketConfig,
075            final ServerSocketFactory serverSocketFactory,
076            final HttpService httpService,
077            final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory,
078            final SSLServerSetupHandler sslSetupHandler,
079            final ExceptionLogger exceptionLogger) {
080        this.port = port;
081        this.ifAddress = ifAddress;
082        this.socketConfig = socketConfig;
083        this.serverSocketFactory = serverSocketFactory;
084        this.httpService = httpService;
085        this.connectionFactory = connectionFactory;
086        this.sslSetupHandler = sslSetupHandler;
087        this.exceptionLogger = exceptionLogger;
088        this.listenerExecutorService = new ThreadPoolExecutor(
089                1, 1, 0L, TimeUnit.MILLISECONDS,
090                new SynchronousQueue<Runnable>(),
091                new ThreadFactoryImpl("HTTP-listener-" + this.port));
092        this.workerThreads = new ThreadGroup("HTTP-workers");
093        this.workerExecutorService = new WorkerPoolExecutor(
094                0, Integer.MAX_VALUE, 1L, TimeUnit.SECONDS,
095                new SynchronousQueue<Runnable>(),
096                new ThreadFactoryImpl("HTTP-worker", this.workerThreads));
097        this.status = new AtomicReference<Status>(Status.READY);
098    }
099
100    public InetAddress getInetAddress() {
101        final ServerSocket localSocket = this.serverSocket;
102        if (localSocket != null) {
103            return localSocket.getInetAddress();
104        } else {
105            return null;
106        }
107    }
108
109    public int getLocalPort() {
110        final ServerSocket localSocket = this.serverSocket;
111        if (localSocket != null) {
112            return localSocket.getLocalPort();
113        } else {
114            return -1;
115        }
116    }
117
118    public void start() throws IOException {
119        if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
120            this.serverSocket = this.serverSocketFactory.createServerSocket(
121                    this.port, this.socketConfig.getBacklogSize(), this.ifAddress);
122            this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress());
123            if (this.socketConfig.getRcvBufSize() > 0) {
124                this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
125            }
126            if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
127                this.sslSetupHandler.initialize((SSLServerSocket) this.serverSocket);
128            }
129            this.requestListener = new RequestListener(
130                    this.socketConfig,
131                    this.serverSocket,
132                    this.httpService,
133                    this.connectionFactory,
134                    this.exceptionLogger,
135                    this.workerExecutorService);
136            this.listenerExecutorService.execute(this.requestListener);
137        }
138    }
139
140    public void stop() {
141        if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) {
142            this.listenerExecutorService.shutdown();
143            this.workerExecutorService.shutdown();
144            final RequestListener local = this.requestListener;
145            if (local != null) {
146                try {
147                    local.terminate();
148                } catch (final IOException ex) {
149                    this.exceptionLogger.log(ex);
150                }
151            }
152            this.workerThreads.interrupt();
153        }
154    }
155
156    public void awaitTermination(final long timeout, final TimeUnit timeUnit) throws InterruptedException {
157        this.workerExecutorService.awaitTermination(timeout, timeUnit);
158    }
159
160    public void shutdown(final long gracePeriod, final TimeUnit timeUnit) {
161        stop();
162        if (gracePeriod > 0) {
163            try {
164                awaitTermination(gracePeriod, timeUnit);
165            } catch (final InterruptedException ex) {
166                Thread.currentThread().interrupt();
167            }
168        }
169        final Set<Worker> workers = this.workerExecutorService.getWorkers();
170        for (final Worker worker: workers) {
171            final HttpServerConnection conn = worker.getConnection();
172            try {
173                conn.shutdown();
174            } catch (final IOException ex) {
175                this.exceptionLogger.log(ex);
176            }
177        }
178    }
179
180}