001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.webserver; 020 021import org.apache.reef.tang.annotations.Parameter; 022import org.apache.reef.util.logging.LoggingScope; 023import org.apache.reef.util.logging.LoggingScopeFactory; 024import org.apache.reef.wake.remote.address.LocalAddressProvider; 025import org.apache.reef.wake.remote.ports.TcpPortProvider; 026import org.apache.reef.wake.remote.ports.parameters.TcpPortRangeBegin; 027import org.mortbay.jetty.Connector; 028import org.mortbay.jetty.Server; 029import org.mortbay.jetty.bio.SocketConnector; 030 031import javax.inject.Inject; 032import java.net.BindException; 033import java.util.Iterator; 034import java.util.logging.Level; 035import java.util.logging.Logger; 036 037/** 038 * HttpServer. It manages Jetty Server and Event Handlers 039 */ 040public final class HttpServerImpl implements HttpServer { 041 /** 042 * Standard Java logger. 043 */ 044 private static final Logger LOG = Logger.getLogger(HttpServerImpl.class.getName()); 045 046 /** 047 * JettyHandler injected in the constructor. 048 */ 049 private JettyHandler jettyHandler; 050 051 /** 052 * Jetty server. 053 */ 054 private final Server server; 055 056 /** 057 * port number used in Jetty Server. 058 */ 059 private final int port; 060 061 /** 062 * Logging scope factory. 063 */ 064 private final LoggingScopeFactory loggingScopeFactory; 065 066 /** 067 * The address provider for the HTTPServer. 068 */ 069 private final LocalAddressProvider addressProvider; 070 071 /** 072 * Constructor of HttpServer that wraps Jetty Server. 073 * 074 * @param jettyHandler 075 * @param portNumber 076 * @throws Exception 077 */ 078 @Inject 079 HttpServerImpl(final JettyHandler jettyHandler, 080 final LocalAddressProvider addressProvider, 081 @Parameter(TcpPortRangeBegin.class)final int portNumber, 082 final TcpPortProvider tcpPortProvider, 083 final LoggingScopeFactory loggingScopeFactory) throws Exception { 084 this.addressProvider = addressProvider; 085 this.loggingScopeFactory = loggingScopeFactory; 086 this.jettyHandler = jettyHandler; 087 int availablePort = portNumber; 088 Server srv = null; 089 090 try (final LoggingScope ls = this.loggingScopeFactory.httpServer()) { 091 092 final Iterator<Integer> ports = tcpPortProvider.iterator(); 093 while (ports.hasNext() && srv == null) { 094 availablePort = ports.next(); 095 srv = tryPort(availablePort); 096 } 097 098 if (srv != null) { 099 this.server = srv; 100 this.port = availablePort; 101 this.server.setHandler(jettyHandler); 102 LOG.log(Level.INFO, "Jetty Server started with port: {0}", availablePort); 103 } else { 104 throw new RuntimeException("Could not find available port for http"); 105 } 106 } 107 } 108 109 private Server tryPort(final int portNumber) throws Exception { 110 Server srv = new Server(); 111 final Connector connector = new SocketConnector(); 112 connector.setHost(addressProvider.getLocalAddress()); 113 connector.setPort(portNumber); 114 srv.addConnector(connector); 115 try { 116 srv.start(); 117 LOG.log(Level.INFO, "Jetty Server started with port: {0}", portNumber); 118 } catch (final BindException ex) { 119 srv = null; 120 LOG.log(Level.FINEST, "Cannot use port: {0}. Will try another", portNumber); 121 } 122 return srv; 123 } 124 125 /** 126 * get a random port number in min and max range. 127 * 128 * @return 129 */ 130 private int getNextPort(final int maxPort, final int minPort) { 131 return minPort + (int) (Math.random() * ((maxPort - minPort) + 1)); 132 } 133 134 /** 135 * It will be called from RuntimeStartHandler. 136 * As the Jetty server has been started at initialization phase, no need to start here. 137 * 138 * @throws Exception 139 */ 140 @Override 141 public void start() throws Exception { 142 } 143 144 /** 145 * stop Jetty Server. It will be called from RuntimeStopHandler 146 * 147 * @throws Exception 148 */ 149 @Override 150 public void stop() throws Exception { 151 server.stop(); 152 } 153 154 @Override 155 public int getPort() { 156 return port; 157 } 158 159 /** 160 * Add a HttpHandler to Jetty Handler. 161 * 162 * @param httpHandler 163 */ 164 @Override 165 public void addHttpHandler(final HttpHandler httpHandler) { 166 LOG.log(Level.INFO, "addHttpHandler: {0}", httpHandler.getUriSpecification()); 167 jettyHandler.addHandler(httpHandler); 168 } 169}