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}