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.nio.bootstrap;
28  
29  import java.io.IOException;
30  import java.net.InetAddress;
31  import java.net.InetSocketAddress;
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.TimeUnit;
35  import java.util.concurrent.atomic.AtomicReference;
36  
37  import org.apache.http.ExceptionLogger;
38  import org.apache.http.impl.nio.DefaultHttpServerIODispatch;
39  import org.apache.http.impl.nio.DefaultNHttpServerConnection;
40  import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
41  import org.apache.http.impl.nio.reactor.IOReactorConfig;
42  import org.apache.http.nio.NHttpConnectionFactory;
43  import org.apache.http.nio.NHttpServerEventHandler;
44  import org.apache.http.nio.reactor.IOEventDispatch;
45  import org.apache.http.nio.reactor.IOReactorException;
46  import org.apache.http.nio.reactor.IOReactorExceptionHandler;
47  import org.apache.http.nio.reactor.ListenerEndpoint;
48  
49  /**
50   * @since 4.4
51   */
52  public class HttpServer {
53  
54      enum Status { READY, ACTIVE, STOPPING }
55  
56      private final int port;
57      private final InetAddress ifAddress;
58      private final IOReactorConfig ioReactorConfig;
59      private final NHttpServerEventHandler serverEventHandler;
60      private final NHttpConnectionFactory<? extends DefaultNHttpServerConnection> connectionFactory;
61      private final ExceptionLogger exceptionLogger;
62      private final ExecutorService listenerExecutorService;
63      private final ThreadGroup dispatchThreads;
64      private final AtomicReference<Status> status;
65      private final DefaultListeningIOReactor ioReactor;
66  
67      private volatile ListenerEndpoint endpoint;
68  
69      HttpServer(
70              final int port,
71              final InetAddress ifAddress,
72              final IOReactorConfig ioReactorConfig,
73              final NHttpServerEventHandler serverEventHandler,
74              final NHttpConnectionFactory<? extends DefaultNHttpServerConnection> connectionFactory,
75              final ExceptionLogger exceptionLogger) {
76          this.port = port;
77          this.ifAddress = ifAddress;
78          this.ioReactorConfig = ioReactorConfig;
79          this.serverEventHandler = serverEventHandler;
80          this.connectionFactory = connectionFactory;
81          this.exceptionLogger = exceptionLogger;
82          this.listenerExecutorService = Executors.newSingleThreadExecutor(
83                  new ThreadFactoryImpl("HTTP-listener-" + this.port));
84          this.dispatchThreads = new ThreadGroup("I/O-dispatchers");
85          try {
86              this.ioReactor = new DefaultListeningIOReactor(
87                      this.ioReactorConfig,
88                      new ThreadFactoryImpl("I/O-dispatch", this.dispatchThreads));
89          } catch (final IOReactorException ex) {
90              throw new IllegalStateException(ex);
91          }
92          this.ioReactor.setExceptionHandler(new IOReactorExceptionHandler() {
93              @Override
94              public boolean handle(final IOException ex) {
95                  exceptionLogger.log(ex);
96                  return false;
97              }
98  
99              @Override
100             public boolean handle(final RuntimeException ex) {
101                 exceptionLogger.log(ex);
102                 return false;
103             }
104         });
105         this.status = new AtomicReference<Status>(Status.READY);
106     }
107 
108     public ListenerEndpoint getEndpoint() {
109         return this.endpoint;
110     }
111 
112     public void start() throws IOException {
113         if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
114             this.endpoint = this.ioReactor.listen(new InetSocketAddress(this.ifAddress, this.port > 0 ? this.port : 0));
115             final IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(
116                     this.serverEventHandler, this.connectionFactory);
117             this.listenerExecutorService.execute(new Runnable() {
118 
119                 @Override
120                 public void run() {
121                     try {
122                         ioReactor.execute(ioEventDispatch);
123                     } catch (final Exception ex) {
124                         exceptionLogger.log(ex);
125                     }
126                 }
127 
128             });
129         }
130     }
131 
132     public void awaitTermination(final long timeout, final TimeUnit timeUnit) throws InterruptedException {
133         this.listenerExecutorService.awaitTermination(timeout, timeUnit);
134     }
135 
136     public void shutdown(final long gracePeriod, final TimeUnit timeUnit) {
137         if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) {
138             try {
139                 this.ioReactor.shutdown(timeUnit.toMillis(gracePeriod));
140             } catch (final IOException ex) {
141                 this.exceptionLogger.log(ex);
142             }
143             this.listenerExecutorService.shutdown();
144         }
145     }
146 
147 }