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