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, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.hadoop.lib.servlet; 020 021 import com.google.common.annotations.VisibleForTesting; 022 import org.apache.hadoop.classification.InterfaceAudience; 023 import org.apache.hadoop.conf.Configuration; 024 import org.apache.hadoop.lib.server.Server; 025 import org.apache.hadoop.lib.server.ServerException; 026 027 import javax.servlet.ServletContextEvent; 028 import javax.servlet.ServletContextListener; 029 import java.net.InetAddress; 030 import java.net.InetSocketAddress; 031 import java.net.UnknownHostException; 032 import java.text.MessageFormat; 033 034 /** 035 * {@link Server} subclass that implements <code>ServletContextListener</code> 036 * and uses its lifecycle to start and stop the server. 037 */ 038 @InterfaceAudience.Private 039 public abstract class ServerWebApp extends Server implements ServletContextListener { 040 041 private static final String HOME_DIR = ".home.dir"; 042 private static final String CONFIG_DIR = ".config.dir"; 043 private static final String LOG_DIR = ".log.dir"; 044 private static final String TEMP_DIR = ".temp.dir"; 045 private static final String HTTP_HOSTNAME = ".http.hostname"; 046 private static final String HTTP_PORT = ".http.port"; 047 048 private static ThreadLocal<String> HOME_DIR_TL = new ThreadLocal<String>(); 049 050 private InetSocketAddress authority; 051 052 /** 053 * Method for testing purposes. 054 */ 055 public static void setHomeDirForCurrentThread(String homeDir) { 056 HOME_DIR_TL.set(homeDir); 057 } 058 059 /** 060 * Constructor for testing purposes. 061 */ 062 protected ServerWebApp(String name, String homeDir, String configDir, String logDir, String tempDir, 063 Configuration config) { 064 super(name, homeDir, configDir, logDir, tempDir, config); 065 } 066 067 /** 068 * Constructor for testing purposes. 069 */ 070 protected ServerWebApp(String name, String homeDir, Configuration config) { 071 super(name, homeDir, config); 072 } 073 074 /** 075 * Constructor. Subclasses must have a default constructor specifying 076 * the server name. 077 * <p/> 078 * The server name is used to resolve the Java System properties that define 079 * the server home, config, log and temp directories. 080 * <p/> 081 * The home directory is looked in the Java System property 082 * <code>#SERVER_NAME#.home.dir</code>. 083 * <p/> 084 * The config directory is looked in the Java System property 085 * <code>#SERVER_NAME#.config.dir</code>, if not defined it resolves to 086 * the <code>#SERVER_HOME_DIR#/conf</code> directory. 087 * <p/> 088 * The log directory is looked in the Java System property 089 * <code>#SERVER_NAME#.log.dir</code>, if not defined it resolves to 090 * the <code>#SERVER_HOME_DIR#/log</code> directory. 091 * <p/> 092 * The temp directory is looked in the Java System property 093 * <code>#SERVER_NAME#.temp.dir</code>, if not defined it resolves to 094 * the <code>#SERVER_HOME_DIR#/temp</code> directory. 095 * 096 * @param name server name. 097 */ 098 public ServerWebApp(String name) { 099 super(name, getHomeDir(name), 100 getDir(name, CONFIG_DIR, getHomeDir(name) + "/conf"), 101 getDir(name, LOG_DIR, getHomeDir(name) + "/log"), 102 getDir(name, TEMP_DIR, getHomeDir(name) + "/temp"), null); 103 } 104 105 /** 106 * Returns the server home directory. 107 * <p/> 108 * It is looked up in the Java System property 109 * <code>#SERVER_NAME#.home.dir</code>. 110 * 111 * @param name the server home directory. 112 * 113 * @return the server home directory. 114 */ 115 static String getHomeDir(String name) { 116 String homeDir = HOME_DIR_TL.get(); 117 if (homeDir == null) { 118 String sysProp = name + HOME_DIR; 119 homeDir = System.getProperty(sysProp); 120 if (homeDir == null) { 121 throw new IllegalArgumentException(MessageFormat.format("System property [{0}] not defined", sysProp)); 122 } 123 } 124 return homeDir; 125 } 126 127 /** 128 * Convenience method that looks for Java System property defining a 129 * diretory and if not present defaults to the specified directory. 130 * 131 * @param name server name, used as prefix of the Java System property. 132 * @param dirType dir type, use as postfix of the Java System property. 133 * @param defaultDir the default directory to return if the Java System 134 * property <code>name + dirType</code> is not defined. 135 * 136 * @return the directory defined in the Java System property or the 137 * the default directory if the Java System property is not defined. 138 */ 139 static String getDir(String name, String dirType, String defaultDir) { 140 String sysProp = name + dirType; 141 return System.getProperty(sysProp, defaultDir); 142 } 143 144 /** 145 * Initializes the <code>ServletContextListener</code> which initializes 146 * the Server. 147 * 148 * @param event servelt context event. 149 */ 150 public void contextInitialized(ServletContextEvent event) { 151 try { 152 init(); 153 } catch (ServerException ex) { 154 event.getServletContext().log("ERROR: " + ex.getMessage()); 155 throw new RuntimeException(ex); 156 } 157 } 158 159 /** 160 * Resolves the host & port InetSocketAddress the web server is listening to. 161 * <p/> 162 * This implementation looks for the following 2 properties: 163 * <ul> 164 * <li>#SERVER_NAME#.http.hostname</li> 165 * <li>#SERVER_NAME#.http.port</li> 166 * </ul> 167 * 168 * @return the host & port InetSocketAddress the web server is listening to. 169 * @throws ServerException thrown if any of the above 2 properties is not defined. 170 */ 171 protected InetSocketAddress resolveAuthority() throws ServerException { 172 String hostnameKey = getName() + HTTP_HOSTNAME; 173 String portKey = getName() + HTTP_PORT; 174 String host = System.getProperty(hostnameKey); 175 String port = System.getProperty(portKey); 176 if (host == null) { 177 throw new ServerException(ServerException.ERROR.S13, hostnameKey); 178 } 179 if (port == null) { 180 throw new ServerException(ServerException.ERROR.S13, portKey); 181 } 182 try { 183 InetAddress add = InetAddress.getByName(host); 184 int portNum = Integer.parseInt(port); 185 return new InetSocketAddress(add, portNum); 186 } catch (UnknownHostException ex) { 187 throw new ServerException(ServerException.ERROR.S14, ex.toString(), ex); 188 } 189 } 190 191 /** 192 * Destroys the <code>ServletContextListener</code> which destroys 193 * the Server. 194 * 195 * @param event servelt context event. 196 */ 197 public void contextDestroyed(ServletContextEvent event) { 198 destroy(); 199 } 200 201 /** 202 * Returns the hostname:port InetSocketAddress the webserver is listening to. 203 * 204 * @return the hostname:port InetSocketAddress the webserver is listening to. 205 */ 206 public InetSocketAddress getAuthority() throws ServerException { 207 synchronized (this) { 208 if (authority == null) { 209 authority = resolveAuthority(); 210 } 211 } 212 return authority; 213 } 214 215 /** 216 * Sets an alternate hostname:port InetSocketAddress to use. 217 * <p/> 218 * For testing purposes. 219 * 220 * @param authority alterante authority. 221 */ 222 @VisibleForTesting 223 public void setAuthority(InetSocketAddress authority) { 224 this.authority = authority; 225 } 226 }