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      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    }