/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java
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 * @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}