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.mortbay.jetty.Server; 025 026import javax.inject.Inject; 027import java.net.BindException; 028import java.util.logging.Level; 029import java.util.logging.Logger; 030 031/** 032 * HttpServer. It manages Jetty Server and Event Handlers 033 */ 034public final class HttpServerImpl implements HttpServer { 035 /** 036 * Standard Java logger. 037 */ 038 private static final Logger LOG = Logger.getLogger(HttpServerImpl.class.getName()); 039 040 /** 041 * JettyHandler injected in the constructor 042 */ 043 private JettyHandler jettyHandler; 044 045 /** 046 * Jetty server. 047 */ 048 private final Server server; 049 050 /** 051 * port number used in Jetty Server 052 */ 053 private final int port; 054 055 /** 056 * Logging scope factory 057 */ 058 private final LoggingScopeFactory loggingScopeFactory; 059 060 /** 061 * Constructor of HttpServer that wraps Jetty Server 062 * 063 * @param jettyHandler 064 * @param portNumber 065 * @throws Exception 066 */ 067 @Inject 068 HttpServerImpl(final JettyHandler jettyHandler, 069 final @Parameter(PortNumber.class) int portNumber, 070 final @Parameter(MaxPortNumber.class) int maxPortNumber, 071 final @Parameter(MinPortNumber.class) int minPortNumber, 072 final @Parameter(MaxRetryAttempts.class) int maxRetryAttempts, 073 final LoggingScopeFactory loggingScopeFactory) throws Exception { 074 075 this.loggingScopeFactory = loggingScopeFactory; 076 try (final LoggingScope ls = this.loggingScopeFactory.httpServer()) { 077 this.jettyHandler = jettyHandler; 078 int port = portNumber; 079 Server srv = null; 080 boolean found = false; 081 for (int attempt = 0; attempt < maxRetryAttempts; ++attempt) { 082 if (attempt > 0) { 083 port = getNextPort(maxPortNumber, minPortNumber); 084 } 085 srv = new Server(port); 086 try { 087 srv.start(); 088 found = true; 089 break; 090 } catch (final BindException ex) { 091 LOG.log(Level.FINEST, "Cannot use port: {0}. Will try another", port); 092 } 093 } 094 095 if (found) { 096 this.server = srv; 097 this.port = port; 098 this.server.setHandler(jettyHandler); 099 LOG.log(Level.INFO, "Jetty Server started with port: {0}", port); 100 } else { 101 throw new RuntimeException("Could not find available port in " + maxRetryAttempts + " attempts"); 102 } 103 } 104 } 105 106 /** 107 * get a random port number in min and max range 108 * 109 * @return 110 */ 111 private int getNextPort(final int maxPort, final int minPort) { 112 return minPort + (int) (Math.random() * ((maxPort - minPort) + 1)); 113 } 114 115 /** 116 * It will be called from RuntimeStartHandler. As the Jetty server has been started at initialization phase, no need to start here. 117 * 118 * @throws Exception 119 */ 120 @Override 121 public void start() throws Exception { 122 } 123 124 /** 125 * stop Jetty Server. It will be called from RuntimeStopHandler 126 * 127 * @throws Exception 128 */ 129 @Override 130 public void stop() throws Exception { 131 server.stop(); 132 } 133 134 @Override 135 public int getPort() { 136 return port; 137 } 138 139 /** 140 * Add a HttpHandler to Jetty Handler 141 * 142 * @param httpHandler 143 */ 144 @Override 145 public void addHttpHandler(final HttpHandler httpHandler) { 146 LOG.log(Level.INFO, "addHttpHandler: {0}", httpHandler.getUriSpecification()); 147 jettyHandler.addHandler(httpHandler); 148 } 149}