View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.bootstrap;
28  
29  import java.io.IOException;
30  import java.net.InetAddress;
31  import java.net.ServerSocket;
32  import java.util.Set;
33  import java.util.concurrent.SynchronousQueue;
34  import java.util.concurrent.ThreadPoolExecutor;
35  import java.util.concurrent.TimeUnit;
36  import java.util.concurrent.atomic.AtomicReference;
37  
38  import javax.net.ServerSocketFactory;
39  import javax.net.ssl.SSLServerSocket;
40  
41  import org.apache.http.ExceptionLogger;
42  import org.apache.http.HttpConnectionFactory;
43  import org.apache.http.HttpServerConnection;
44  import org.apache.http.config.SocketConfig;
45  import org.apache.http.impl.DefaultBHttpServerConnection;
46  import org.apache.http.protocol.HttpService;
47  
48  /**
49   * @since 4.4
50   */
51  public class HttpServer {
52  
53      enum Status { READY, ACTIVE, STOPPING }
54  
55      private final int port;
56      private final InetAddress ifAddress;
57      private final SocketConfig socketConfig;
58      private final ServerSocketFactory serverSocketFactory;
59      private final HttpService httpService;
60      private final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory;
61      private final SSLServerSetupHandler sslSetupHandler;
62      private final ExceptionLogger exceptionLogger;
63      private final ThreadPoolExecutor listenerExecutorService;
64      private final ThreadGroup workerThreads;
65      private final WorkerPoolExecutor workerExecutorService;
66      private final AtomicReference<Status> status;
67  
68      private volatile ServerSocket serverSocket;
69      private volatile RequestListener requestListener;
70  
71      HttpServer(
72              final int port,
73              final InetAddress ifAddress,
74              final SocketConfig socketConfig,
75              final ServerSocketFactory serverSocketFactory,
76              final HttpService httpService,
77              final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory,
78              final SSLServerSetupHandler sslSetupHandler,
79              final ExceptionLogger exceptionLogger) {
80          this.port = port;
81          this.ifAddress = ifAddress;
82          this.socketConfig = socketConfig;
83          this.serverSocketFactory = serverSocketFactory;
84          this.httpService = httpService;
85          this.connectionFactory = connectionFactory;
86          this.sslSetupHandler = sslSetupHandler;
87          this.exceptionLogger = exceptionLogger;
88          this.listenerExecutorService = new ThreadPoolExecutor(
89                  1, 1, 0L, TimeUnit.MILLISECONDS,
90                  new SynchronousQueue<Runnable>(),
91                  new ThreadFactoryImpl("HTTP-listener-" + this.port));
92          this.workerThreads = new ThreadGroup("HTTP-workers");
93          this.workerExecutorService = new WorkerPoolExecutor(
94                  0, Integer.MAX_VALUE, 1L, TimeUnit.SECONDS,
95                  new SynchronousQueue<Runnable>(),
96                  new ThreadFactoryImpl("HTTP-worker", this.workerThreads));
97          this.status = new AtomicReference<Status>(Status.READY);
98      }
99  
100     public InetAddress getInetAddress() {
101         final ServerSocket localSocket = this.serverSocket;
102         return localSocket != null ? localSocket.getInetAddress() : null;
103     }
104 
105     public int getLocalPort() {
106         final ServerSocket localSocket = this.serverSocket;
107         return localSocket != null ? localSocket.getLocalPort() : -1;
108     }
109 
110     public void start() throws IOException {
111         if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
112             this.serverSocket = this.serverSocketFactory.createServerSocket(
113                     this.port, this.socketConfig.getBacklogSize(), this.ifAddress);
114             this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress());
115             if (this.socketConfig.getRcvBufSize() > 0) {
116                 this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
117             }
118             if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
119                 this.sslSetupHandler.initialize((SSLServerSocket) this.serverSocket);
120             }
121             this.requestListener = new RequestListener(
122                     this.socketConfig,
123                     this.serverSocket,
124                     this.httpService,
125                     this.connectionFactory,
126                     this.exceptionLogger,
127                     this.workerExecutorService);
128             this.listenerExecutorService.execute(this.requestListener);
129         }
130     }
131 
132     public void stop() {
133         if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) {
134             this.listenerExecutorService.shutdown();
135             this.workerExecutorService.shutdown();
136             final RequestListener local = this.requestListener;
137             if (local != null) {
138                 try {
139                     local.terminate();
140                 } catch (final IOException ex) {
141                     this.exceptionLogger.log(ex);
142                 }
143             }
144             this.workerThreads.interrupt();
145         }
146     }
147 
148     public void awaitTermination(final long timeout, final TimeUnit timeUnit) throws InterruptedException {
149         this.workerExecutorService.awaitTermination(timeout, timeUnit);
150     }
151 
152     public void shutdown(final long gracePeriod, final TimeUnit timeUnit) {
153         stop();
154         if (gracePeriod > 0) {
155             try {
156                 awaitTermination(gracePeriod, timeUnit);
157             } catch (final InterruptedException ex) {
158                 Thread.currentThread().interrupt();
159             }
160         }
161         final Set<Worker> workers = this.workerExecutorService.getWorkers();
162         for (final Worker worker: workers) {
163             final HttpServerConnection conn = worker.getConnection();
164             try {
165                 conn.shutdown();
166             } catch (final IOException ex) {
167                 this.exceptionLogger.log(ex);
168             }
169         }
170     }
171 
172 }