PageRenderTime 100ms CodeModel.GetById 49ms app.highlight 29ms RepoModel.GetById 3ms app.codeStats 0ms

/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java

https://github.com/rbodkin/hadoop-common
Java | 215 lines | 144 code | 20 blank | 51 comment | 17 complexity | b026a07569c3966bd99797757b61212e MD5 | raw file
  1/**
  2* Licensed to the Apache Software Foundation (ASF) under one
  3* or more contributor license agreements.  See the NOTICE file
  4* distributed with this work for additional information
  5* regarding copyright ownership.  The ASF licenses this file
  6* to you under the Apache License, Version 2.0 (the
  7* "License"); you may not use this file except in compliance
  8* with the License.  You may obtain a copy of the License at
  9*
 10*     http://www.apache.org/licenses/LICENSE-2.0
 11*
 12* Unless required by applicable law or agreed to in writing, software
 13* distributed under the License is distributed on an "AS IS" BASIS,
 14* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15* See the License for the specific language governing permissions and
 16* limitations under the License.
 17*/
 18
 19package org.apache.hadoop.yarn.webapp;
 20
 21import static com.google.common.base.Preconditions.*;
 22import com.google.inject.AbstractModule;
 23import com.google.inject.Guice;
 24import com.google.inject.Injector;
 25import com.google.inject.Module;
 26import com.google.inject.servlet.GuiceFilter;
 27
 28import java.net.ConnectException;
 29import java.net.URL;
 30import org.apache.commons.lang.StringUtils;
 31
 32import org.apache.hadoop.conf.Configuration;
 33import org.apache.hadoop.http.HttpServer;
 34
 35import org.slf4j.Logger;
 36import org.slf4j.LoggerFactory;
 37
 38/**
 39 * Helpers to create an embedded webapp.
 40 *
 41 * <h4>Quick start:</h4>
 42 * <pre>
 43 *   WebApp wa = WebApps.$for(myApp).start();</pre>
 44 * Starts a webapp with default routes binds to 0.0.0.0 (all network interfaces)
 45 * on an ephemeral port, which can be obtained with:<pre>
 46 *   int port = wa.port();</pre>
 47 * <h4>With more options:</h4>
 48 * <pre>
 49 *   WebApp wa = WebApps.$for(myApp).at(address, port).
 50 *                        with(configuration).
 51 *                        start(new WebApp() {
 52 *     &#064;Override public void setup() {
 53 *       route("/foo/action", FooController.class);
 54 *       route("/foo/:id", FooController.class, "show");
 55 *     }
 56 *   });</pre>
 57 */
 58public class WebApps {
 59  static final Logger LOG = LoggerFactory.getLogger(WebApps.class);
 60
 61  public static class Builder<T> {
 62    final String name;
 63    final Class<T> api;
 64    final T application;
 65    String bindAddress = "0.0.0.0";
 66    int port = 0;
 67    boolean findPort = false;
 68    Configuration conf;
 69    boolean devMode = false;
 70
 71    Builder(String name, Class<T> api, T application) {
 72      this.name = name;
 73      this.api = api;
 74      this.application = application;
 75    }
 76
 77    public Builder<T> at(String bindAddress) {
 78      String[] parts = StringUtils.split(bindAddress, ':');
 79      if (parts.length == 2) {
 80        return at(parts[0], Integer.parseInt(parts[1]), true);
 81      }
 82      return at(bindAddress, 0, true);
 83    }
 84
 85    public Builder<T> at(int port) {
 86      return at("0.0.0.0", port, false);
 87    }
 88
 89    public Builder<T> at(String address, int port, boolean findPort) {
 90      this.bindAddress = checkNotNull(address, "bind address");
 91      this.port = port;
 92      this.findPort = findPort;
 93      return this;
 94    }
 95
 96    public Builder<T> with(Configuration conf) {
 97      this.conf = conf;
 98      return this;
 99    }
100
101    public Builder<T> inDevMode() {
102      devMode = true;
103      return this;
104    }
105
106    public WebApp start(WebApp webapp) {
107      if (webapp == null) {
108        webapp = new WebApp() {
109          @Override
110          public void setup() {
111            // Defaults should be fine in usual cases
112          }
113        };
114      }
115      webapp.setName(name);
116      if (conf == null) {
117        conf = new Configuration();
118      }
119      try {
120        if (application != null) {
121          webapp.setHostClass(application.getClass());
122        } else {
123          String cls = inferHostClass();
124          LOG.debug("setting webapp host class to {}", cls);
125          webapp.setHostClass(Class.forName(cls));
126        }
127        if (devMode) {
128          if (port > 0) {
129            try {
130              new URL("http://localhost:"+ port +"/__stop").getContent();
131              LOG.info("stopping existing webapp instance");
132              Thread.sleep(100);
133            } catch (ConnectException e) {
134              LOG.info("no existing webapp instance found: {}", e.toString());
135            } catch (Exception e) {
136              // should not be fatal
137              LOG.warn("error stopping existing instance: {}", e.toString());
138            }
139          } else {
140            LOG.error("dev mode does NOT work with ephemeral port!");
141            System.exit(1);
142          }
143        }
144        HttpServer server =
145            new HttpServer(name, bindAddress, port, findPort, conf);
146        server.addGlobalFilter("guice", GuiceFilter.class.getName(), null);
147        webapp.setConf(conf);
148        webapp.setHttpServer(server);
149        server.start();
150        LOG.info("Web app /"+ name +" started at "+ server.getPort());
151      } catch (Exception e) {
152        throw new WebAppException("Error starting http server", e);
153      }
154      Injector injector = Guice.createInjector(webapp, new AbstractModule() {
155        @Override
156        protected void configure() {
157          if (api != null) {
158            bind(api).toInstance(application);
159          }
160        }
161      });
162      LOG.info("Registered webapp guice modules");
163      // save a guice filter instance for webapp stop (mostly for unit tests)
164      webapp.setGuiceFilter(injector.getInstance(GuiceFilter.class));
165      if (devMode) {
166        injector.getInstance(Dispatcher.class).setDevMode(devMode);
167        LOG.info("in dev mode!");
168      }
169      return webapp;
170    }
171
172    public WebApp start() {
173      return start(null);
174    }
175
176    private String inferHostClass() {
177      String thisClass = this.getClass().getName();
178      Throwable t = new Throwable();
179      for (StackTraceElement e : t.getStackTrace()) {
180        if (e.getClassName().equals(thisClass)) continue;
181        return e.getClassName();
182      }
183      LOG.warn("could not infer host class from", t);
184      return thisClass;
185    }
186  }
187
188  /**
189   * Create a new webapp builder.
190   * @see WebApps for a complete example
191   * @param <T> application (holding the embedded webapp) type
192   * @param prefix of the webapp
193   * @param api the api class for the application
194   * @param app the application instance
195   * @return a webapp builder
196   */
197  public static <T> Builder<T> $for(String prefix, Class<T> api, T app) {
198    return new Builder<T>(prefix, api, app);
199  }
200
201  // Short cut mostly for tests/demos
202  @SuppressWarnings("unchecked")
203  public static <T> Builder<T> $for(String prefix, T app) {
204    return $for(prefix, (Class<T>)app.getClass(), app);
205  }
206
207  // Ditto
208  public static <T> Builder<T> $for(T app) {
209    return $for("", app);
210  }
211
212  public static <T> Builder<T> $for(String prefix) {
213    return $for(prefix, null, null);
214  }
215}