/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java
Java | 221 lines | 149 code | 21 blank | 51 comment | 17 complexity | ac5ea1d9cfc7d80e723001e16ec27b12 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 * @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 Module[] modules;
71
72 Builder(String name, Class<T> api, T application) {
73 this.name = name;
74 this.api = api;
75 this.application = application;
76 }
77
78 public Builder<T> at(String bindAddress) {
79 String[] parts = StringUtils.split(bindAddress, ':');
80 if (parts.length == 2) {
81 return at(parts[0], Integer.parseInt(parts[1]), true);
82 }
83 return at(bindAddress, 0, true);
84 }
85
86 public Builder<T> at(int port) {
87 return at("0.0.0.0", port, false);
88 }
89
90 public Builder<T> at(String address, int port, boolean findPort) {
91 this.bindAddress = checkNotNull(address, "bind address");
92 this.port = port;
93 this.findPort = findPort;
94 return this;
95 }
96
97 public Builder<T> with(Configuration conf) {
98 this.conf = conf;
99 return this;
100 }
101
102 public Builder<T> with(Module... modules) {
103 this.modules = modules; // OK
104 return this;
105 }
106
107 public Builder<T> inDevMode() {
108 devMode = true;
109 return this;
110 }
111
112 public WebApp start(WebApp webapp) {
113 if (webapp == null) {
114 webapp = new WebApp() {
115 @Override
116 public void setup() {
117 // Defaults should be fine in usual cases
118 }
119 };
120 }
121 webapp.setName(name);
122 if (conf == null) {
123 conf = new Configuration();
124 }
125 try {
126 if (application != null) {
127 webapp.setHostClass(application.getClass());
128 } else {
129 String cls = inferHostClass();
130 LOG.debug("setting webapp host class to {}", cls);
131 webapp.setHostClass(Class.forName(cls));
132 }
133 if (devMode) {
134 if (port > 0) {
135 try {
136 new URL("http://localhost:"+ port +"/__stop").getContent();
137 LOG.info("stopping existing webapp instance");
138 Thread.sleep(100);
139 } catch (ConnectException e) {
140 LOG.info("no existing webapp instance found: {}", e.toString());
141 } catch (Exception e) {
142 // should not be fatal
143 LOG.warn("error stopping existing instance: {}", e.toString());
144 }
145 } else {
146 LOG.error("dev mode does NOT work with ephemeral port!");
147 System.exit(1);
148 }
149 }
150 HttpServer server =
151 new HttpServer(name, bindAddress, port, findPort, conf);
152 server.addGlobalFilter("guice", GuiceFilter.class.getName(), null);
153 webapp.setConf(conf);
154 webapp.setHttpServer(server);
155 server.start();
156 LOG.info("Web app /"+ name +" started at "+ server.getPort());
157 } catch (Exception e) {
158 throw new WebAppException("Error starting http server", e);
159 }
160 Injector injector = Guice.createInjector(webapp, new AbstractModule() {
161 @Override
162 protected void configure() {
163 if (api != null) {
164 bind(api).toInstance(application);
165 }
166 }
167 });
168 LOG.info("Registered webapp guice modules");
169 // save a guice filter instance for webapp stop (mostly for unit tests)
170 webapp.setGuiceFilter(injector.getInstance(GuiceFilter.class));
171 if (devMode) {
172 injector.getInstance(Dispatcher.class).setDevMode(devMode);
173 LOG.info("in dev mode!");
174 }
175 return webapp;
176 }
177
178 public WebApp start() {
179 return start(null);
180 }
181
182 private String inferHostClass() {
183 String thisClass = this.getClass().getName();
184 Throwable t = new Throwable();
185 for (StackTraceElement e : t.getStackTrace()) {
186 if (e.getClassName().equals(thisClass)) continue;
187 return e.getClassName();
188 }
189 LOG.warn("could not infer host class from", t);
190 return thisClass;
191 }
192 }
193
194 /**
195 * Create a new webapp builder.
196 * @see WebApps for a complete example
197 * @param <T> application (holding the embedded webapp) type
198 * @param prefix of the webapp
199 * @param api the api class for the application
200 * @param app the application instance
201 * @return a webapp builder
202 */
203 public static <T> Builder<T> $for(String prefix, Class<T> api, T app) {
204 return new Builder<T>(prefix, api, app);
205 }
206
207 // Short cut mostly for tests/demos
208 @SuppressWarnings("unchecked")
209 public static <T> Builder<T> $for(String prefix, T app) {
210 return $for(prefix, (Class<T>)app.getClass(), app);
211 }
212
213 // Ditto
214 public static <T> Builder<T> $for(T app) {
215 return $for("", app);
216 }
217
218 public static <T> Builder<T> $for(String prefix) {
219 return $for(prefix, null, null);
220 }
221}