View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.util;
22  
23  import java.io.FileNotFoundException;
24  import java.io.IOException;
25  import java.lang.reflect.Method;
26  import java.net.URL;
27  import java.util.Map;
28  
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.HBaseConfiguration;
31  import org.apache.hadoop.http.HttpServer;
32  import org.mortbay.jetty.handler.ContextHandlerCollection;
33  import org.mortbay.jetty.servlet.Context;
34  import org.mortbay.jetty.servlet.DefaultServlet;
35  
36  /**
37   * Create a Jetty embedded server to answer http requests. The primary goal
38   * is to serve up status information for the server.
39   * There are three contexts:
40   *   "/stacks/" -> points to stack trace
41   *   "/static/" -> points to common static files (src/hbase-webapps/static)
42   *   "/" -> the jsp server code from (src/hbase-webapps/<name>)
43   */
44  public class InfoServer extends HttpServer {
45    private final Configuration config;
46  
47    /**
48     * Create a status server on the given port.
49     * The jsp scripts are taken from src/hbase-webapps/<code>name<code>.
50     * @param name The name of the server
51     * @param bindAddress address to bind to
52     * @param port The port to use on the server
53     * @param findPort whether the server should start at the given port and
54     * increment by 1 until it finds a free port.
55     * @throws IOException e
56     */
57    public InfoServer(String name, String bindAddress, int port, boolean findPort,
58        final Configuration c)
59    throws IOException {
60      super(name, bindAddress, port, findPort, c);
61      this.config = c;
62      fixupLogsServletLocation();
63    }
64  
65    /**
66     * Fixup where the logs app points, make it point at hbase logs rather than
67     * hadoop logs.
68     */
69    private void fixupLogsServletLocation() {
70      // Must be same as up in hadoop.
71      final String logsContextPath = "/logs";
72      // Now, put my logs in place of hadoops... disable old one first.
73      Context oldLogsContext = null;
74      for (Map.Entry<Context, Boolean> e : defaultContexts.entrySet()) {
75        if (e.getKey().getContextPath().equals(logsContextPath)) {
76          oldLogsContext = e.getKey();
77          break;
78        }
79      }
80      if (oldLogsContext != null) {
81        this.defaultContexts.put(oldLogsContext, Boolean.FALSE);
82      }
83      // Now do my logs.
84      // Set up the context for "/logs/" if "hbase.log.dir" property is defined.
85      String logDir = System.getProperty("hbase.log.dir");
86      if (logDir != null) {
87        // This is a little presumptious but seems to work.
88        Context logContext =
89          new Context((ContextHandlerCollection)this.webServer.getHandler(),
90            logsContextPath);
91        logContext.setResourceBase(logDir);
92        logContext.addServlet(DefaultServlet.class, "/");
93        HttpServerUtil.constrainHttpMethods(logContext);
94        defaultContexts.put(logContext, true);
95      }
96    }
97  
98    /**
99     * Get the pathname to the webapps files.
100    * @param appName eg "secondary" or "datanode"
101    * @return the pathname as a URL
102    * @throws FileNotFoundException if 'webapps' directory cannot be found on CLASSPATH.
103    */
104   protected String getWebAppsPath(String appName) throws FileNotFoundException {
105     // Copied from the super-class.
106     String resourceName = "hbase-webapps/" + appName;
107     URL url = getClass().getClassLoader().getResource(resourceName);
108     if (url == null)
109       throw new FileNotFoundException(resourceName + " not found in CLASSPATH");
110     String urlString = url.toString();
111     return urlString.substring(0, urlString.lastIndexOf('/'));
112   }
113 
114   /**
115    * Get the pathname to the <code>path</code> files.
116    * @return the pathname as a URL
117    */
118   protected String getWebAppsPath() throws IOException {
119     // Hack: webapps is not a unique enough element to find in CLASSPATH
120     // We'll more than likely find the hadoop webapps dir.  So, instead
121     // look for the 'master' webapp in the webapps subdir.  That should
122     // get us the hbase context.  Presumption is that place where the
123     // master webapp resides is where we want this InfoServer picking up
124     // web applications.
125     final String master = "master";
126     String p = getWebAppsPath(master);
127     // Now strip master off the end if it is present
128     if(p.endsWith(master)) {
129       return p.substring(0, p.lastIndexOf(master));
130     }
131     return p;
132   }
133 }