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