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         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 (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 (InterruptedException ex) {
166                 Thread.currentThread().interrupt();
167             }
168         }
169         final Set<Worker> workers = this.workerExecutorService.getWorkers();
170         for (Worker worker: workers) {
171             final HttpServerConnection conn = worker.getConnection();
172             try {
173                 conn.shutdown();
174             } catch (IOException ex) {
175                 this.exceptionLogger.log(ex);
176             }
177         }
178     }
179 
180 }