/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. package org.apache.hadoop.yarn.webapp;
  19. import static com.google.common.base.Preconditions.*;
  20. import com.google.inject.AbstractModule;
  21. import com.google.inject.Guice;
  22. import com.google.inject.Injector;
  23. import com.google.inject.Module;
  24. import com.google.inject.servlet.GuiceFilter;
  25. import java.net.ConnectException;
  26. import java.net.URL;
  27. import org.apache.commons.lang.StringUtils;
  28. import org.apache.hadoop.conf.Configuration;
  29. import org.apache.hadoop.http.HttpServer;
  30. import org.slf4j.Logger;
  31. import org.slf4j.LoggerFactory;
  32. /**
  33. * Helpers to create an embedded webapp.
  34. *
  35. * <h4>Quick start:</h4>
  36. * <pre>
  37. * WebApp wa = WebApps.$for(myApp).start();</pre>
  38. * Starts a webapp with default routes binds to 0.0.0.0 (all network interfaces)
  39. * on an ephemeral port, which can be obtained with:<pre>
  40. * int port = wa.port();</pre>
  41. * <h4>With more options:</h4>
  42. * <pre>
  43. * WebApp wa = WebApps.$for(myApp).at(address, port).
  44. * with(configuration).
  45. * start(new WebApp() {
  46. * &#064;Override public void setup() {
  47. * route("/foo/action", FooController.class);
  48. * route("/foo/:id", FooController.class, "show");
  49. * }
  50. * });</pre>
  51. */
  52. public class WebApps {
  53. static final Logger LOG = LoggerFactory.getLogger(WebApps.class);
  54. public static class Builder<T> {
  55. final String name;
  56. final Class<T> api;
  57. final T application;
  58. String bindAddress = "0.0.0.0";
  59. int port = 0;
  60. boolean findPort = false;
  61. Configuration conf;
  62. boolean devMode = false;
  63. Builder(String name, Class<T> api, T application) {
  64. this.name = name;
  65. this.api = api;
  66. this.application = application;
  67. }
  68. public Builder<T> at(String bindAddress) {
  69. String[] parts = StringUtils.split(bindAddress, ':');
  70. if (parts.length == 2) {
  71. return at(parts[0], Integer.parseInt(parts[1]), true);
  72. }
  73. return at(bindAddress, 0, true);
  74. }
  75. public Builder<T> at(int port) {
  76. return at("0.0.0.0", port, false);
  77. }
  78. public Builder<T> at(String address, int port, boolean findPort) {
  79. this.bindAddress = checkNotNull(address, "bind address");
  80. this.port = port;
  81. this.findPort = findPort;
  82. return this;
  83. }
  84. public Builder<T> with(Configuration conf) {
  85. this.conf = conf;
  86. return this;
  87. }
  88. public Builder<T> inDevMode() {
  89. devMode = true;
  90. return this;
  91. }
  92. public WebApp start(WebApp webapp) {
  93. if (webapp == null) {
  94. webapp = new WebApp() {
  95. @Override
  96. public void setup() {
  97. // Defaults should be fine in usual cases
  98. }
  99. };
  100. }
  101. webapp.setName(name);
  102. if (conf == null) {
  103. conf = new Configuration();
  104. }
  105. try {
  106. if (application != null) {
  107. webapp.setHostClass(application.getClass());
  108. } else {
  109. String cls = inferHostClass();
  110. LOG.debug("setting webapp host class to {}", cls);
  111. webapp.setHostClass(Class.forName(cls));
  112. }
  113. if (devMode) {
  114. if (port > 0) {
  115. try {
  116. new URL("http://localhost:"+ port +"/__stop").getContent();
  117. LOG.info("stopping existing webapp instance");
  118. Thread.sleep(100);
  119. } catch (ConnectException e) {
  120. LOG.info("no existing webapp instance found: {}", e.toString());
  121. } catch (Exception e) {
  122. // should not be fatal
  123. LOG.warn("error stopping existing instance: {}", e.toString());
  124. }
  125. } else {
  126. LOG.error("dev mode does NOT work with ephemeral port!");
  127. System.exit(1);
  128. }
  129. }
  130. HttpServer server =
  131. new HttpServer(name, bindAddress, port, findPort, conf);
  132. server.addGlobalFilter("guice", GuiceFilter.class.getName(), null);
  133. webapp.setConf(conf);
  134. webapp.setHttpServer(server);
  135. server.start();
  136. LOG.info("Web app /"+ name +" started at "+ server.getPort());
  137. } catch (Exception e) {
  138. throw new WebAppException("Error starting http server", e);
  139. }
  140. Injector injector = Guice.createInjector(webapp, new AbstractModule() {
  141. @Override
  142. protected void configure() {
  143. if (api != null) {
  144. bind(api).toInstance(application);
  145. }
  146. }
  147. });
  148. LOG.info("Registered webapp guice modules");
  149. // save a guice filter instance for webapp stop (mostly for unit tests)
  150. webapp.setGuiceFilter(injector.getInstance(GuiceFilter.class));
  151. if (devMode) {
  152. injector.getInstance(Dispatcher.class).setDevMode(devMode);
  153. LOG.info("in dev mode!");
  154. }
  155. return webapp;
  156. }
  157. public WebApp start() {
  158. return start(null);
  159. }
  160. private String inferHostClass() {
  161. String thisClass = this.getClass().getName();
  162. Throwable t = new Throwable();
  163. for (StackTraceElement e : t.getStackTrace()) {
  164. if (e.getClassName().equals(thisClass)) continue;
  165. return e.getClassName();
  166. }
  167. LOG.warn("could not infer host class from", t);
  168. return thisClass;
  169. }
  170. }
  171. /**
  172. * Create a new webapp builder.
  173. * @see WebApps for a complete example
  174. * @param <T> application (holding the embedded webapp) type
  175. * @param prefix of the webapp
  176. * @param api the api class for the application
  177. * @param app the application instance
  178. * @return a webapp builder
  179. */
  180. public static <T> Builder<T> $for(String prefix, Class<T> api, T app) {
  181. return new Builder<T>(prefix, api, app);
  182. }
  183. // Short cut mostly for tests/demos
  184. @SuppressWarnings("unchecked")
  185. public static <T> Builder<T> $for(String prefix, T app) {
  186. return $for(prefix, (Class<T>)app.getClass(), app);
  187. }
  188. // Ditto
  189. public static <T> Builder<T> $for(T app) {
  190. return $for("", app);
  191. }
  192. public static <T> Builder<T> $for(String prefix) {
  193. return $for(prefix, null, null);
  194. }
  195. }