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.nio.bootstrap; 028 029import java.io.IOException; 030import java.net.InetAddress; 031import java.net.InetSocketAddress; 032import java.util.concurrent.ExecutorService; 033import java.util.concurrent.Executors; 034import java.util.concurrent.TimeUnit; 035import java.util.concurrent.atomic.AtomicReference; 036 037import org.apache.http.ExceptionLogger; 038import org.apache.http.impl.nio.DefaultHttpServerIODispatch; 039import org.apache.http.impl.nio.DefaultNHttpServerConnection; 040import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor; 041import org.apache.http.impl.nio.reactor.IOReactorConfig; 042import org.apache.http.nio.NHttpConnectionFactory; 043import org.apache.http.nio.NHttpServerEventHandler; 044import org.apache.http.nio.reactor.IOEventDispatch; 045import org.apache.http.nio.reactor.IOReactorException; 046import org.apache.http.nio.reactor.IOReactorExceptionHandler; 047import org.apache.http.nio.reactor.ListenerEndpoint; 048 049/** 050 * @since 4.4 051 */ 052public class HttpServer { 053 054 enum Status { READY, ACTIVE, STOPPING } 055 056 private final int port; 057 private final InetAddress ifAddress; 058 private final IOReactorConfig ioReactorConfig; 059 private final NHttpServerEventHandler serverEventHandler; 060 private final NHttpConnectionFactory<? extends DefaultNHttpServerConnection> connectionFactory; 061 private final ExceptionLogger exceptionLogger; 062 private final ExecutorService listenerExecutorService; 063 private final ThreadGroup dispatchThreads; 064 private final AtomicReference<Status> status; 065 private final DefaultListeningIOReactor ioReactor; 066 067 private volatile ListenerEndpoint endpoint; 068 069 HttpServer( 070 final int port, 071 final InetAddress ifAddress, 072 final IOReactorConfig ioReactorConfig, 073 final NHttpServerEventHandler serverEventHandler, 074 final NHttpConnectionFactory<? extends DefaultNHttpServerConnection> connectionFactory, 075 final ExceptionLogger exceptionLogger) { 076 this.port = port; 077 this.ifAddress = ifAddress; 078 this.ioReactorConfig = ioReactorConfig; 079 this.serverEventHandler = serverEventHandler; 080 this.connectionFactory = connectionFactory; 081 this.exceptionLogger = exceptionLogger; 082 this.listenerExecutorService = Executors.newSingleThreadExecutor( 083 new ThreadFactoryImpl("HTTP-listener-" + this.port)); 084 this.dispatchThreads = new ThreadGroup("I/O-dispatchers"); 085 try { 086 this.ioReactor = new DefaultListeningIOReactor( 087 this.ioReactorConfig, 088 new ThreadFactoryImpl("I/O-dispatch", this.dispatchThreads)); 089 } catch (final IOReactorException ex) { 090 throw new IllegalStateException(ex); 091 } 092 this.ioReactor.setExceptionHandler(new IOReactorExceptionHandler() { 093 @Override 094 public boolean handle(final IOException ex) { 095 exceptionLogger.log(ex); 096 return false; 097 } 098 099 @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}