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.List;
33  import java.util.concurrent.ExecutorService;
34  import java.util.concurrent.Executors;
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 ExecutorService listenerExecutorService;
64      private final ThreadGroup workerThreads;
65      private final ExecutorService 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 = Executors.newSingleThreadExecutor(
89                  new ThreadFactoryImpl("HTTP-listener-" + this.port));
90          this.workerThreads = new ThreadGroup("HTTP-workers");
91          this.workerExecutorService = Executors.newCachedThreadPool(
92                  new ThreadFactoryImpl("HTTP-worker", this.workerThreads));
93          this.status = new AtomicReference<Status>(Status.READY);
94      }
95  
96      public InetAddress getInetAddress() {
97          final ServerSocket localSocket = this.serverSocket;
98          if (localSocket != null) {
99              return localSocket.getInetAddress();
100         } else {
101             return null;
102         }
103     }
104 
105     public int getLocalPort() {
106         final ServerSocket localSocket = this.serverSocket;
107         if (localSocket != null) {
108             return localSocket.getLocalPort();
109         } else {
110             return -1;
111         }
112     }
113 
114     public void start() throws IOException {
115         if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
116             this.serverSocket = this.serverSocketFactory.createServerSocket(
117                     this.port, this.socketConfig.getBacklogSize(), this.ifAddress);
118             this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress());
119             if (this.socketConfig.getRcvBufSize() > 0) {
120                 this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
121             }
122             if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
123                 this.sslSetupHandler.initialize((SSLServerSocket) this.serverSocket);
124             }
125             this.requestListener = new RequestListener(
126                     this.socketConfig,
127                     this.serverSocket,
128                     this.httpService,
129                     this.connectionFactory,
130                     this.exceptionLogger,
131                     this.workerExecutorService);
132             this.listenerExecutorService.execute(this.requestListener);
133         }
134     }
135 
136     public void stop() {
137         if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) {
138             final RequestListener local = this.requestListener;
139             if (local != null) {
140                 try {
141                     local.terminate();
142                 } catch (IOException ex) {
143                     this.exceptionLogger.log(ex);
144                 }
145             }
146             this.workerThreads.interrupt();
147             this.listenerExecutorService.shutdown();
148             this.workerExecutorService.shutdown();
149         }
150     }
151 
152     public void awaitTermination(final long timeout, final TimeUnit timeUnit) throws InterruptedException {
153         this.workerExecutorService.awaitTermination(timeout, timeUnit);
154     }
155 
156     public void shutdown(final long gracePeriod, final TimeUnit timeUnit) {
157         stop();
158         if (gracePeriod > 0) {
159             try {
160                 awaitTermination(gracePeriod, timeUnit);
161             } catch (InterruptedException ex) {
162                 Thread.currentThread().interrupt();
163             }
164         }
165         final List<Runnable> runnables = this.workerExecutorService.shutdownNow();
166         for (Runnable runnable: runnables) {
167             if (runnable instanceof Worker) {
168                 final Worker worker = (Worker) runnable;
169                 final HttpServerConnection conn = worker.getConnection();
170                 try {
171                     conn.shutdown();
172                 } catch (IOException ex) {
173                     this.exceptionLogger.log(ex);
174                 }
175             }
176         }
177     }
178 
179 }