You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by wf...@apache.org on 2014/07/30 02:01:29 UTC
git commit: Replace HttpModule from twitter.common with our own code.
Repository: incubator-aurora
Updated Branches:
refs/heads/master 4439be3d7 -> 6449b9200
Replace HttpModule from twitter.common with our own code.
Bugs closed: AURORA-606
Reviewed at https://reviews.apache.org/r/24059/
Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/6449b920
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/6449b920
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/6449b920
Branch: refs/heads/master
Commit: 6449b9200617c7e0d4f41237ef21489ec9cdd7e3
Parents: 4439be3
Author: Bill Farner <wf...@apache.org>
Authored: Tue Jul 29 16:59:12 2014 -0700
Committer: Bill Farner <wf...@apache.org>
Committed: Tue Jul 29 16:59:12 2014 -0700
----------------------------------------------------------------------
build.gradle | 4 +-
.../apache/aurora/scheduler/app/AppModule.java | 4 +-
.../aurora/scheduler/app/SchedulerMain.java | 2 -
.../aurora/scheduler/http/AbortCallback.java | 28 ++
.../scheduler/http/JettyServerModule.java | 390 +++++++++++++++++++
.../aurora/scheduler/http/ServletModule.java | 263 -------------
.../scheduler/http/JettyServerModuleTest.java | 159 ++++++++
.../apache/aurora/scheduler/http/MnameTest.java | 2 +-
.../scheduler/http/ServletFilterTest.java | 2 +-
.../scheduler/http/ServletModuleTest.java | 160 --------
.../aurora/scheduler/http/api/ApiBetaTest.java | 4 +-
11 files changed, 584 insertions(+), 434 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index 22ac386..2756e84 100644
--- a/build.gradle
+++ b/build.gradle
@@ -176,8 +176,6 @@ dependencies {
compile "org.slf4j:slf4j-api:${slf4jRev}"
compile "org.slf4j:slf4j-jdk14:${slf4jRev}"
compile 'com.twitter.common.logging:log4j:0.0.7'
- compile 'com.twitter.common.webassets:bootstrap:0.0.4'
- compile 'com.twitter.common.webassets:jquery:0.0.4'
compile 'com.twitter.common.zookeeper.guice:client-flagged:0.0.5'
compile 'com.twitter.common.zookeeper.guice:client:0.0.5'
compile 'com.twitter.common.zookeeper:candidate:0.0.64'
@@ -187,7 +185,6 @@ dependencies {
compile 'com.twitter.common.zookeeper:singleton-service:0.0.85'
compile 'com.twitter.common:application-http:0.0.59'
compile 'com.twitter.common:application-module-applauncher:0.0.51'
- compile 'com.twitter.common:application-module-http:0.0.62'
compile 'com.twitter.common:application-module-lifecycle:0.0.48'
compile 'com.twitter.common:application-module-log:0.0.56'
compile 'com.twitter.common:application-module-stats:0.0.44'
@@ -201,6 +198,7 @@ dependencies {
compile 'com.twitter.common:io:0.0.53'
compile 'com.twitter.common:jdk-logging:0.0.44'
compile 'com.twitter.common:logging:0.0.61'
+ compile 'com.twitter.common:net-http-handlers-time-series:0.0.51'
compile 'com.twitter.common:net-util:0.0.80'
compile 'com.twitter.common:quantity:0.0.71'
compile 'com.twitter.common:stats:0.0.91'
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/app/AppModule.java b/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
index 30b1ba6..61ba4b2 100644
--- a/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
@@ -48,7 +48,7 @@ import org.apache.aurora.scheduler.SchedulerModule;
import org.apache.aurora.scheduler.async.AsyncModule;
import org.apache.aurora.scheduler.events.PubsubEventModule;
import org.apache.aurora.scheduler.filter.SchedulingFilterImpl;
-import org.apache.aurora.scheduler.http.ServletModule;
+import org.apache.aurora.scheduler.http.JettyServerModule;
import org.apache.aurora.scheduler.metadata.MetadataModule;
import org.apache.aurora.scheduler.quota.QuotaModule;
import org.apache.aurora.scheduler.sla.SlaModule;
@@ -113,7 +113,7 @@ class AppModule extends AbstractModule {
install(new AsyncStatsModule());
install(new MetadataModule());
install(new QuotaModule());
- install(new ServletModule());
+ install(new JettyServerModule());
install(new SchedulerModule());
install(new StateModule());
install(new SlaModule());
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java b/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
index f429eda..ec31c49 100644
--- a/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
+++ b/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
@@ -29,7 +29,6 @@ import com.google.inject.Module;
import com.twitter.common.application.AbstractApplication;
import com.twitter.common.application.AppLauncher;
import com.twitter.common.application.Lifecycle;
-import com.twitter.common.application.modules.HttpModule;
import com.twitter.common.application.modules.LocalServiceRegistry;
import com.twitter.common.application.modules.LogModule;
import com.twitter.common.application.modules.StatsModule;
@@ -132,7 +131,6 @@ public class SchedulerMain extends AbstractApplication {
return ImmutableList.<Module>builder()
.add(new LogModule())
- .add(new HttpModule())
.add(new StatsModule())
.add(new AppModule(clusterName, serverSetPath, zkClientConfig, statsURLPrefix))
.addAll(getExtraModules())
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/main/java/org/apache/aurora/scheduler/http/AbortCallback.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/AbortCallback.java b/src/main/java/org/apache/aurora/scheduler/http/AbortCallback.java
new file mode 100644
index 0000000..b5eed69
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/http/AbortCallback.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http;
+
+import java.util.logging.Logger;
+
+/**
+ * Default handler to invoke when the process is being instructed to exit immediately.
+ */
+class AbortCallback implements Runnable {
+ private static final Logger LOG = Logger.getLogger(AbortCallback.class.getName());
+
+ @Override public void run() {
+ LOG.info("ABORTING PROCESS IMMEDIATELY!");
+ Runtime.getRuntime().halt(0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java b/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
new file mode 100644
index 0000000..de49a1c
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
@@ -0,0 +1,390 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import javax.annotation.Nonnegative;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.servlet.http.HttpServlet;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Resources;
+import com.google.common.net.MediaType;
+import com.google.inject.AbstractModule;
+import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+import com.google.inject.servlet.GuiceFilter;
+import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter;
+import com.sun.jersey.guice.JerseyServletModule;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import com.twitter.common.application.http.DefaultQuitHandler;
+import com.twitter.common.application.http.GraphViewer;
+import com.twitter.common.application.http.HttpAssetConfig;
+import com.twitter.common.application.http.HttpFilterConfig;
+import com.twitter.common.application.http.HttpServletConfig;
+import com.twitter.common.application.http.Registration;
+import com.twitter.common.application.modules.LifecycleModule;
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+import com.twitter.common.base.Command;
+import com.twitter.common.base.ExceptionalCommand;
+import com.twitter.common.base.ExceptionalSupplier;
+import com.twitter.common.base.MoreSuppliers;
+import com.twitter.common.net.http.HttpServerDispatch;
+import com.twitter.common.net.http.RequestLogger;
+import com.twitter.common.net.http.handlers.AbortHandler;
+import com.twitter.common.net.http.handlers.ContentionPrinter;
+import com.twitter.common.net.http.handlers.HealthHandler;
+import com.twitter.common.net.http.handlers.LogConfig;
+import com.twitter.common.net.http.handlers.QuitHandler;
+import com.twitter.common.net.http.handlers.StringTemplateServlet;
+import com.twitter.common.net.http.handlers.ThreadStackPrinter;
+import com.twitter.common.net.http.handlers.TimeSeriesDataSource;
+import com.twitter.common.net.http.handlers.VarsHandler;
+import com.twitter.common.net.http.handlers.VarsJsonHandler;
+import com.twitter.common.net.pool.DynamicHostSet.MonitorException;
+
+import org.apache.aurora.scheduler.http.api.ApiBeta;
+import org.mortbay.jetty.RequestLog;
+import org.mortbay.servlet.GzipFilter;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS;
+import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS;
+import static com.sun.jersey.api.json.JSONConfiguration.FEATURE_POJO_MAPPING;
+import static com.twitter.common.application.modules.LocalServiceRegistry.LocalService;
+
+/**
+ * Binding module for scheduler HTTP servlets.
+ * <p>
+ * TODO(wfarner): Continue improvements here by simplifying serving of static assets. Jetty's
+ * DefaultServlet can take over this responsibility.
+ */
+public class JettyServerModule extends AbstractModule {
+
+ private static final Logger LOG = Logger.getLogger(JettyServerModule.class.getName());
+
+ @Nonnegative
+ @CmdLine(name = "http_port",
+ help = "The port to start an HTTP server on. Default value will choose a random port.")
+ protected static final Arg<Integer> HTTP_PORT = Arg.create(0);
+
+ @CmdLine(name = "enable_cors_support", help = "Enable CORS support for thrift end points.")
+ private static final Arg<Boolean> ENABLE_CORS_SUPPORT = Arg.create(false);
+
+ // More info on CORS can be found at http://enable-cors.org/index.html
+ @CmdLine(name = "enable_cors_for",
+ help = "List of domains for which CORS support should be enabled.")
+ private static final Arg<String> ENABLE_CORS_FOR = Arg.create("*");
+
+ private static final Map<String, String> CONTAINER_PARAMS = ImmutableMap.of(
+ FEATURE_POJO_MAPPING, Boolean.TRUE.toString(),
+ PROPERTY_CONTAINER_REQUEST_FILTERS, GZIPContentEncodingFilter.class.getName(),
+ PROPERTY_CONTAINER_RESPONSE_FILTERS, GZIPContentEncodingFilter.class.getName());
+
+ @Override
+ protected void configure() {
+ bind(Runnable.class)
+ .annotatedWith(Names.named(AbortHandler.ABORT_HANDLER_KEY))
+ .to(AbortCallback.class);
+ bind(AbortCallback.class).in(Singleton.class);
+ bind(Runnable.class).annotatedWith(Names.named(QuitHandler.QUIT_HANDLER_KEY))
+ .to(DefaultQuitHandler.class);
+ bind(DefaultQuitHandler.class).in(Singleton.class);
+ bind(new TypeLiteral<ExceptionalSupplier<Boolean, ?>>() { })
+ .annotatedWith(Names.named(HealthHandler.HEALTH_CHECKER_KEY))
+ .toInstance(MoreSuppliers.ofInstance(true));
+
+ bindConstant().annotatedWith(StringTemplateServlet.CacheTemplates.class).to(true);
+
+ bind(HttpServerDispatch.class).in(Singleton.class);
+ bind(RequestLog.class).to(RequestLogger.class);
+ Registration.registerServlet(binder(), "/abortabortabort", AbortHandler.class, true);
+ Registration.registerServlet(binder(), "/contention", ContentionPrinter.class, false);
+ Registration.registerServlet(binder(), "/graphdata", TimeSeriesDataSource.class, true);
+ Registration.registerServlet(binder(), "/health", HealthHandler.class, true);
+ Registration.registerServlet(binder(), "/healthz", HealthHandler.class, true);
+ Registration.registerServlet(binder(), "/logconfig", LogConfig.class, false);
+ Registration.registerServlet(binder(), "/quitquitquit", QuitHandler.class, true);
+ Registration.registerServlet(binder(), "/threads", ThreadStackPrinter.class, false);
+ Registration.registerServlet(binder(), "/vars", VarsHandler.class, false);
+ Registration.registerServlet(binder(), "/vars.json", VarsJsonHandler.class, false);
+
+ GraphViewer.registerResources(binder());
+
+ LifecycleModule.bindServiceRunner(binder(), HttpServerLauncher.class);
+
+ // Ensure at least an empty filter set is bound.
+ Registration.getFilterBinder(binder());
+
+ // Ensure at least an empty set of additional links is bound.
+ Registration.getEndpointBinder(binder());
+
+ // Register /api end point
+ Registration.registerServlet(binder(), "/api", SchedulerAPIServlet.class, true);
+
+ // NOTE: GzipFilter is applied only to /api instead of globally because the Jersey-managed
+ // servlets have a conflicting filter applied to them.
+ Registration.registerServletFilter(binder(), GzipFilter.class, "/api/*");
+ Registration.registerServletFilter(binder(), GzipFilter.class, "/apibetabeta/*");
+ Registration.registerServletFilter(binder(), GuiceFilter.class, "/*");
+
+ install(new JerseyServletModule() {
+ private void registerJerseyEndpoint(String indexPath, Class<?>... servlets) {
+ filter(indexPath + "*").through(LeaderRedirectFilter.class);
+ filter(indexPath + "*").through(GuiceContainer.class, CONTAINER_PARAMS);
+ Registration.registerEndpoint(binder(), indexPath);
+ for (Class<?> servlet : servlets) {
+ bind(servlet);
+ }
+ }
+
+ @Override
+ protected void configureServlets() {
+ bind(HttpStatsFilter.class).in(Singleton.class);
+ filter("/scheduler*").through(HttpStatsFilter.class);
+ bind(LeaderRedirectFilter.class).in(Singleton.class);
+ filterRegex("/scheduler(?:/.*)?").through(LeaderRedirectFilter.class);
+
+ // Add CORS support for all /api end points.
+ if (ENABLE_CORS_SUPPORT.get()) {
+ bind(CorsFilter.class).toInstance(new CorsFilter(ENABLE_CORS_FOR.get()));
+ filter("/api*").through(CorsFilter.class);
+ }
+
+ registerJerseyEndpoint("/apibeta", ApiBeta.class);
+ registerJerseyEndpoint("/cron", Cron.class);
+ registerJerseyEndpoint("/locks", Locks.class);
+ registerJerseyEndpoint("/maintenance", Maintenance.class);
+ registerJerseyEndpoint("/mname", Mname.class);
+ registerJerseyEndpoint("/offers", Offers.class);
+ registerJerseyEndpoint("/pendingtasks", PendingTasks.class);
+ registerJerseyEndpoint("/quotas", Quotas.class);
+ registerJerseyEndpoint("/slaves", Slaves.class);
+ registerJerseyEndpoint("/structdump", StructDump.class);
+ registerJerseyEndpoint("/utilization", Utilization.class);
+ }
+ });
+
+ // Static assets.
+ registerJQueryAssets();
+ registerBootstrapAssets();
+
+ registerAsset("assets/images/viz.png", "/images/viz.png");
+ registerAsset("assets/images/aurora.png", "/images/aurora.png");
+ registerAsset("assets/images/aurora_logo.png", "/images/aurora_logo.png");
+
+ registerUIClient();
+
+ bind(LeaderRedirect.class).in(Singleton.class);
+ LifecycleModule.bindStartupAction(binder(), RedirectMonitor.class);
+ }
+
+ private void registerJQueryAssets() {
+ registerAsset("bower_components/jquery/dist/jquery.js", "/js/jquery.min.js", false);
+ }
+
+ private void registerBootstrapAssets() {
+ final String BOOTSTRAP_PATH = "bower_components/bootstrap/";
+
+ registerAsset(BOOTSTRAP_PATH + "dist/js/bootstrap.min.js", "/js/bootstrap.min.js", false);
+ registerAsset(BOOTSTRAP_PATH + "dist/css/bootstrap.min.css", "/css/bootstrap.min.css", false);
+
+ registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.eot",
+ "/fonts/glyphicons-halflings-regular.eot",
+ false);
+ registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.svg",
+ "/fonts/glyphicons-halflings-regular.svg",
+ false);
+ registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.ttf",
+ "/fonts/glyphicons-halflings-regular.ttf",
+ false);
+ registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.woff",
+ "/fonts/glyphicons-halflings-regular.woff",
+ false);
+ }
+
+ /**
+ * A function to handle all assets related to the UI client.
+ */
+ private void registerUIClient() {
+ registerAsset("bower_components/smart-table/Smart-Table.debug.js", "/js/smartTable.js", false);
+ registerAsset("bower_components/angular/angular.js", "/js/angular.js", false);
+ registerAsset("bower_components/angular-route/angular-route.js", "/js/angular-route.js", false);
+ registerAsset("bower_components/underscore/underscore.js", "/js/underscore.js", false);
+ registerAsset("bower_components/momentjs/moment.js", "/js/moment.js", false);
+ registerAsset("bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js",
+ "/js/ui-bootstrap-tpls.js",
+ false);
+
+ registerAsset("ReadOnlyScheduler.js", "/js/readOnlyScheduler.js", false);
+ registerAsset("api_types.js", "/js/apiTypes.js", false);
+ registerAsset("thrift.js", "/js/thrift.js", false);
+
+ registerAsset("ui/index.html", "/scheduler", true);
+ Registration.registerEndpoint(binder(), "/scheduler");
+
+ registerAsset("ui/roleLink.html", "/roleLink.html");
+ registerAsset("ui/roleEnvLink.html", "/roleEnvLink.html");
+ registerAsset("ui/jobLink.html", "/jobLink.html");
+ registerAsset("ui/home.html", "/home.html");
+ registerAsset("ui/role.html", "/role.html");
+ registerAsset("ui/breadcrumb.html", "/breadcrumb.html");
+ registerAsset("ui/error.html", "/error.html");
+ registerAsset("ui/job.html", "/job.html");
+ registerAsset("ui/taskSandbox.html", "/taskSandbox.html");
+ registerAsset("ui/taskStatus.html", "/taskStatus.html");
+ registerAsset("ui/taskLink.html", "/taskLink.html");
+ registerAsset("ui/schedulingDetail.html", "/schedulingDetail.html");
+ registerAsset("ui/groupSummary.html", "/groupSummary.html");
+ registerAsset("ui/configSummary.html", "/configSummary.html");
+
+ registerAsset("ui/css/app.css", "/css/app.css");
+
+ registerAsset("ui/js/app.js", "/js/app.js");
+ registerAsset("ui/js/controllers.js", "/js/controllers.js");
+ registerAsset("ui/js/directives.js", "/js/directives.js");
+ registerAsset("ui/js/services.js", "/js/services.js");
+ registerAsset("ui/js/filters.js", "/js/filters.js");
+ }
+
+ private void registerAsset(String resourceLocation, String registerLocation) {
+ registerAsset(resourceLocation, registerLocation, true);
+ }
+
+ private void registerAsset(String resourceLocation, String registerLocation, boolean isRelative) {
+ String mediaType = getMediaType(resourceLocation).toString();
+
+ if (isRelative) {
+ Registration.registerHttpAsset(
+ binder(),
+ registerLocation,
+ JettyServerModule.class,
+ resourceLocation,
+ mediaType,
+ true);
+ } else {
+ Registration.registerHttpAsset(
+ binder(),
+ registerLocation,
+ Resources.getResource(resourceLocation),
+ mediaType,
+ true);
+ }
+ }
+
+ private MediaType getMediaType(String filePath) {
+ if (filePath.endsWith(".png")) {
+ return MediaType.PNG;
+ } else if (filePath.endsWith(".js")) {
+ return MediaType.JAVASCRIPT_UTF_8;
+ } else if (filePath.endsWith(".html")) {
+ return MediaType.HTML_UTF_8;
+ } else if (filePath.endsWith(".css")) {
+ return MediaType.CSS_UTF_8;
+ } else if (filePath.endsWith(".svg")) {
+ return MediaType.SVG_UTF_8;
+ } else if (filePath.endsWith(".ttf")
+ || filePath.endsWith(".eot")
+ || filePath.endsWith(".woff")) {
+
+ // MediaType doesn't have any mime types for fonts. Instead of magic strings, we let the
+ // browser interpret the mime type and modern browsers can do this well.
+ return MediaType.ANY_TYPE;
+ } else {
+ throw new IllegalArgumentException("Could not determine media type for " + filePath);
+ }
+ }
+
+ static class RedirectMonitor implements ExceptionalCommand<MonitorException> {
+
+ private final LeaderRedirect redirector;
+
+ @Inject
+ RedirectMonitor(LeaderRedirect redirector) {
+ this.redirector = Objects.requireNonNull(redirector);
+ }
+
+ @Override
+ public void execute() throws MonitorException {
+ redirector.monitor();
+ }
+ }
+
+ // TODO(wfarner): Use guava's Service to enforce the lifecycle of this.
+ public static final class HttpServerLauncher implements LifecycleModule.ServiceRunner {
+ private final HttpServerDispatch httpServer;
+ private final Set<HttpServletConfig> httpServlets;
+ private final Set<HttpAssetConfig> httpAssets;
+ private final Set<HttpFilterConfig> httpFilters;
+ private final Set<String> additionalIndexLinks;
+ private final Injector injector;
+
+ @Inject
+ HttpServerLauncher(
+ HttpServerDispatch httpServer,
+ Set<HttpServletConfig> httpServlets,
+ Set<HttpAssetConfig> httpAssets,
+ Set<HttpFilterConfig> httpFilters,
+ @Registration.IndexLink Set<String> additionalIndexLinks,
+ Injector injector) {
+
+ this.httpServer = checkNotNull(httpServer);
+ this.httpServlets = checkNotNull(httpServlets);
+ this.httpAssets = checkNotNull(httpAssets);
+ this.httpFilters = checkNotNull(httpFilters);
+ this.additionalIndexLinks = checkNotNull(additionalIndexLinks);
+ this.injector = checkNotNull(injector);
+ }
+
+ @Override
+ public LocalService launch() {
+ if (!httpServer.listen(HTTP_PORT.get())) {
+ throw new IllegalStateException("Failed to start HTTP server, all servlets disabled.");
+ }
+
+ for (HttpServletConfig config : httpServlets) {
+ HttpServlet handler = injector.getInstance(config.handlerClass);
+ httpServer.registerHandler(config.path, handler, config.params, config.silent);
+ }
+
+ for (HttpAssetConfig config : httpAssets) {
+ httpServer.registerHandler(config.path, config.handler, null, config.silent);
+ }
+
+ for (HttpFilterConfig filter : httpFilters) {
+ httpServer.registerFilter(filter.filterClass, filter.pathSpec, filter.dispatch);
+ }
+
+ for (String indexLink : additionalIndexLinks) {
+ httpServer.registerIndexLink(indexLink);
+ }
+
+ Command shutdown = new Command() {
+ @Override public void execute() {
+ LOG.info("Shutting down embedded http server");
+ httpServer.stop();
+ }
+ };
+
+ return LocalService.auxiliaryService("http", httpServer.getPort(), shutdown);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/main/java/org/apache/aurora/scheduler/http/ServletModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/ServletModule.java b/src/main/java/org/apache/aurora/scheduler/http/ServletModule.java
deleted file mode 100644
index 5c0fc2c..0000000
--- a/src/main/java/org/apache/aurora/scheduler/http/ServletModule.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.aurora.scheduler.http;
-
-import java.util.Map;
-import java.util.Objects;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Resources;
-import com.google.common.net.MediaType;
-import com.google.inject.AbstractModule;
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-import com.google.inject.servlet.GuiceFilter;
-import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter;
-import com.sun.jersey.guice.JerseyServletModule;
-import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
-import com.twitter.common.application.http.Registration;
-import com.twitter.common.application.modules.LifecycleModule;
-import com.twitter.common.application.modules.LocalServiceRegistry;
-import com.twitter.common.args.Arg;
-import com.twitter.common.args.CmdLine;
-import com.twitter.common.base.ExceptionalCommand;
-import com.twitter.common.net.pool.DynamicHostSet;
-import com.twitter.common.net.pool.DynamicHostSet.MonitorException;
-import com.twitter.thrift.ServiceInstance;
-
-import org.apache.aurora.scheduler.http.api.ApiBeta;
-import org.mortbay.servlet.GzipFilter;
-
-import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS;
-import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS;
-import static com.sun.jersey.api.json.JSONConfiguration.FEATURE_POJO_MAPPING;
-
-/**
- * Binding module for scheduler HTTP servlets.
- */
-public class ServletModule extends AbstractModule {
-
- @CmdLine(name = "enable_cors_support", help = "Enable CORS support for thrift end points.")
- private static final Arg<Boolean> ENABLE_CORS_SUPPORT = Arg.create(false);
-
- // More info on CORS can be found at http://enable-cors.org/index.html
- @CmdLine(name = "enable_cors_for",
- help = "List of domains for which CORS support should be enabled.")
- private static final Arg<String> ENABLE_CORS_FOR = Arg.create("*");
-
- private static final Map<String, String> CONTAINER_PARAMS = ImmutableMap.of(
- FEATURE_POJO_MAPPING, Boolean.TRUE.toString(),
- PROPERTY_CONTAINER_REQUEST_FILTERS, GZIPContentEncodingFilter.class.getName(),
- PROPERTY_CONTAINER_RESPONSE_FILTERS, GZIPContentEncodingFilter.class.getName());
-
- @Override
- protected void configure() {
- // Register /api end point
- Registration.registerServlet(binder(), "/api", SchedulerAPIServlet.class, true);
-
- // NOTE: GzipFilter is applied only to /api instead of globally because the Jersey-managed
- // servlets have a conflicting filter applied to them.
- Registration.registerServletFilter(binder(), GzipFilter.class, "/api/*");
- // TODO(wfarner): Add a unit test to validate gzip behavior.
- Registration.registerServletFilter(binder(), GzipFilter.class, "/apibeta");
-
- // Bindings required for the leader redirector.
- requireBinding(LocalServiceRegistry.class);
- requireBinding(Key.get(new TypeLiteral<DynamicHostSet<ServiceInstance>>() { }));
- Registration.registerServletFilter(binder(), GuiceFilter.class, "/*");
- install(new JerseyServletModule() {
- private void registerJerseyEndpoint(String indexPath, Class<?>... servlets) {
- filter(indexPath + "*").through(LeaderRedirectFilter.class);
- filter(indexPath + "*").through(GuiceContainer.class, CONTAINER_PARAMS);
- Registration.registerEndpoint(binder(), indexPath);
- for (Class<?> servlet : servlets) {
- bind(servlet);
- }
- }
-
- @Override
- protected void configureServlets() {
- bind(HttpStatsFilter.class).in(Singleton.class);
- filter("/scheduler*").through(HttpStatsFilter.class);
- bind(LeaderRedirectFilter.class).in(Singleton.class);
- filterRegex("/scheduler(?:/.*)?").through(LeaderRedirectFilter.class);
-
- // Add CORS support for all /api end points.
- if (ENABLE_CORS_SUPPORT.get()) {
- bind(CorsFilter.class).toInstance(new CorsFilter(ENABLE_CORS_FOR.get()));
- filter("/api*").through(CorsFilter.class);
- }
-
- registerJerseyEndpoint("/apibeta", ApiBeta.class);
- registerJerseyEndpoint("/cron", Cron.class);
- registerJerseyEndpoint("/locks", Locks.class);
- registerJerseyEndpoint("/maintenance", Maintenance.class);
- registerJerseyEndpoint("/mname", Mname.class);
- registerJerseyEndpoint("/offers", Offers.class);
- registerJerseyEndpoint("/pendingtasks", PendingTasks.class);
- registerJerseyEndpoint("/quotas", Quotas.class);
- registerJerseyEndpoint("/slaves", Slaves.class);
- registerJerseyEndpoint("/structdump", StructDump.class);
- registerJerseyEndpoint("/utilization", Utilization.class);
- }
- });
-
- // Static assets.
- registerJQueryAssets();
- registerBootstrapAssets();
-
- registerAsset("assets/images/viz.png", "/images/viz.png");
- registerAsset("assets/images/aurora.png", "/images/aurora.png");
- registerAsset("assets/images/aurora_logo.png", "/images/aurora_logo.png");
-
- registerUIClient();
-
- bind(LeaderRedirect.class).in(Singleton.class);
- LifecycleModule.bindStartupAction(binder(), RedirectMonitor.class);
- }
-
- private void registerJQueryAssets() {
- registerAsset("bower_components/jquery/dist/jquery.js", "/js/jquery.min.js", false);
- }
-
- private void registerBootstrapAssets() {
- final String BOOTSTRAP_PATH = "bower_components/bootstrap/";
-
- registerAsset(BOOTSTRAP_PATH + "dist/js/bootstrap.min.js", "/js/bootstrap.min.js", false);
- registerAsset(BOOTSTRAP_PATH + "dist/css/bootstrap.min.css", "/css/bootstrap.min.css", false);
-
- registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.eot",
- "/fonts/glyphicons-halflings-regular.eot",
- false);
- registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.svg",
- "/fonts/glyphicons-halflings-regular.svg",
- false);
- registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.ttf",
- "/fonts/glyphicons-halflings-regular.ttf",
- false);
- registerAsset(BOOTSTRAP_PATH + "dist/fonts/glyphicons-halflings-regular.woff",
- "/fonts/glyphicons-halflings-regular.woff",
- false);
- }
-
- /**
- * A function to handle all assets related to the UI client.
- */
- private void registerUIClient() {
- registerAsset("bower_components/smart-table/Smart-Table.debug.js", "/js/smartTable.js", false);
- registerAsset("bower_components/angular/angular.js", "/js/angular.js", false);
- registerAsset("bower_components/angular-route/angular-route.js", "/js/angular-route.js", false);
- registerAsset("bower_components/underscore/underscore.js", "/js/underscore.js", false);
- registerAsset("bower_components/momentjs/moment.js", "/js/moment.js", false);
- registerAsset("bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js",
- "/js/ui-bootstrap-tpls.js",
- false);
-
- registerAsset("ReadOnlyScheduler.js", "/js/readOnlyScheduler.js", false);
- registerAsset("api_types.js", "/js/apiTypes.js", false);
- registerAsset("thrift.js", "/js/thrift.js", false);
-
- registerAsset("ui/index.html", "/scheduler", true);
- Registration.registerEndpoint(binder(), "/scheduler");
-
- registerAsset("ui/roleLink.html", "/roleLink.html");
- registerAsset("ui/roleEnvLink.html", "/roleEnvLink.html");
- registerAsset("ui/jobLink.html", "/jobLink.html");
- registerAsset("ui/home.html", "/home.html");
- registerAsset("ui/role.html", "/role.html");
- registerAsset("ui/breadcrumb.html", "/breadcrumb.html");
- registerAsset("ui/error.html", "/error.html");
- registerAsset("ui/job.html", "/job.html");
- registerAsset("ui/taskSandbox.html", "/taskSandbox.html");
- registerAsset("ui/taskStatus.html", "/taskStatus.html");
- registerAsset("ui/taskLink.html", "/taskLink.html");
- registerAsset("ui/schedulingDetail.html", "/schedulingDetail.html");
- registerAsset("ui/groupSummary.html", "/groupSummary.html");
- registerAsset("ui/configSummary.html", "/configSummary.html");
-
- registerAsset("ui/css/app.css", "/css/app.css");
-
- registerAsset("ui/js/app.js", "/js/app.js");
- registerAsset("ui/js/controllers.js", "/js/controllers.js");
- registerAsset("ui/js/directives.js", "/js/directives.js");
- registerAsset("ui/js/services.js", "/js/services.js");
- registerAsset("ui/js/filters.js", "/js/filters.js");
- }
-
- private void registerAsset(String resourceLocation, String registerLocation) {
- registerAsset(resourceLocation, registerLocation, true);
- }
-
- private void registerAsset(String resourceLocation, String registerLocation, boolean isRelative) {
- String mediaType = getMediaType(resourceLocation).toString();
-
- if (isRelative) {
- Registration.registerHttpAsset(
- binder(),
- registerLocation,
- ServletModule.class,
- resourceLocation,
- mediaType,
- true);
- } else {
- Registration.registerHttpAsset(
- binder(),
- registerLocation,
- Resources.getResource(resourceLocation),
- mediaType,
- true);
- }
- }
-
- private MediaType getMediaType(String filePath) {
- if (filePath.endsWith(".png")) {
- return MediaType.PNG;
- } else if (filePath.endsWith(".js")) {
- return MediaType.JAVASCRIPT_UTF_8;
- } else if (filePath.endsWith(".html")) {
- return MediaType.HTML_UTF_8;
- } else if (filePath.endsWith(".css")) {
- return MediaType.CSS_UTF_8;
- } else if (filePath.endsWith(".svg")) {
- return MediaType.SVG_UTF_8;
- } else if (filePath.endsWith(".ttf")
- || filePath.endsWith(".eot")
- || filePath.endsWith(".woff")) {
-
- // MediaType doesn't have any mime types for fonts. Instead of magic strings, we let the
- // browser interpret the mime type and modern browsers can do this well.
- return MediaType.ANY_TYPE;
- } else {
- throw new IllegalArgumentException("Could not determine media type for " + filePath);
- }
- }
-
- static class RedirectMonitor implements ExceptionalCommand<MonitorException> {
-
- private final LeaderRedirect redirector;
-
- @Inject
- RedirectMonitor(LeaderRedirect redirector) {
- this.redirector = Objects.requireNonNull(redirector);
- }
-
- @Override
- public void execute() throws MonitorException {
- redirector.monitor();
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/test/java/org/apache/aurora/scheduler/http/JettyServerModuleTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/JettyServerModuleTest.java b/src/test/java/org/apache/aurora/scheduler/http/JettyServerModuleTest.java
new file mode 100644
index 0000000..62dce07
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/http/JettyServerModuleTest.java
@@ -0,0 +1,159 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http;
+
+import java.net.InetSocketAddress;
+
+import javax.ws.rs.core.MediaType;
+
+import com.google.common.base.Throwables;
+import com.google.common.testing.TearDown;
+import com.google.common.util.concurrent.RateLimiter;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.json.JSONConfiguration;
+import com.twitter.common.application.ShutdownRegistry.ShutdownRegistryImpl;
+import com.twitter.common.application.StartupRegistry;
+import com.twitter.common.application.modules.LifecycleModule;
+import com.twitter.common.application.modules.LocalServiceRegistry;
+import com.twitter.common.application.modules.LogModule;
+import com.twitter.common.application.modules.StatsModule;
+import com.twitter.common.base.Command;
+import com.twitter.common.net.pool.DynamicHostSet;
+import com.twitter.common.net.pool.DynamicHostSet.HostChangeMonitor;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.BackoffStrategy;
+import com.twitter.thrift.ServiceInstance;
+
+import org.apache.aurora.gen.AuroraAdmin;
+import org.apache.aurora.gen.ServerInfo;
+import org.apache.aurora.scheduler.async.OfferQueue;
+import org.apache.aurora.scheduler.async.RescheduleCalculator;
+import org.apache.aurora.scheduler.async.TaskGroups.TaskGroupsSettings;
+import org.apache.aurora.scheduler.async.TaskScheduler;
+import org.apache.aurora.scheduler.cron.CronJobManager;
+import org.apache.aurora.scheduler.http.api.GsonMessageBodyHandler;
+import org.apache.aurora.scheduler.quota.QuotaManager;
+import org.apache.aurora.scheduler.state.LockManager;
+import org.apache.aurora.scheduler.state.SchedulerCore;
+import org.apache.aurora.scheduler.storage.Storage;
+import org.apache.aurora.scheduler.storage.entities.IServerInfo;
+import org.apache.aurora.scheduler.storage.testing.StorageTestUtil;
+import org.easymock.Capture;
+import org.junit.Before;
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * TODO(wfarner): Break apart ServletModule so test setup isn't so involved.
+ * TODO(wfarner): Come up with an approach for these tests that doesn't require starting an actual
+ * HTTP server for each test case.
+ *
+ */
+public abstract class JettyServerModuleTest extends EasyMockTest {
+
+ private Injector injector;
+ protected StorageTestUtil storage;
+ protected InetSocketAddress httpServer;
+ protected Capture<HostChangeMonitor<ServiceInstance>> schedulerWatcher;
+ protected AuroraAdmin.Iface thrift;
+
+ @Before
+ public void setUp() throws Exception {
+ storage = new StorageTestUtil(this);
+ final DynamicHostSet<ServiceInstance> schedulers =
+ createMock(new Clazz<DynamicHostSet<ServiceInstance>>() {
+ });
+
+ injector = Guice.createInjector(
+ new JettyServerModule(),
+ new LogModule(),
+ new StatsModule(),
+ new LifecycleModule(),
+ new AbstractModule() {
+ <T> T bindMock(Class<T> clazz) {
+ T mock = createMock(clazz);
+ bind(clazz).toInstance(mock);
+ return mock;
+ }
+
+ @Override
+ protected void configure() {
+ bind(Storage.class).toInstance(storage.storage);
+ bind(IServerInfo.class).toInstance(IServerInfo.build(new ServerInfo()
+ .setClusterName("unittest")
+ .setThriftAPIVersion(100)
+ .setStatsUrlPrefix("none")));
+ bind(TaskGroupsSettings.class).toInstance(
+ new TaskGroupsSettings(bindMock(BackoffStrategy.class), RateLimiter.create(1000)));
+
+ bind(new TypeLiteral<DynamicHostSet<ServiceInstance>>() { }).toInstance(schedulers);
+ thrift = bindMock(AuroraAdmin.Iface.class);
+ bindMock(CronJobManager.class);
+ bindMock(LockManager.class);
+ bindMock(OfferQueue.class);
+ bindMock(QuotaManager.class);
+ bindMock(RescheduleCalculator.class);
+ bindMock(SchedulerCore.class);
+ bindMock(TaskScheduler.class);
+ bindMock(Thread.UncaughtExceptionHandler.class);
+ }
+ });
+
+ schedulerWatcher = createCapture();
+ expect(schedulers.watch(capture(schedulerWatcher))).andReturn(createMock(Command.class));
+ }
+
+ protected void replayAndStart() {
+ control.replay();
+
+ final ShutdownRegistryImpl shutdownRegistry = injector.getInstance(ShutdownRegistryImpl.class);
+ addTearDown(new TearDown() {
+ @Override
+ public void tearDown() {
+ shutdownRegistry.execute();
+ }
+ });
+ try {
+ injector.getInstance(StartupRegistry.class).execute();
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+ LocalServiceRegistry serviceRegistry = injector.getInstance(LocalServiceRegistry.class);
+ httpServer = serviceRegistry.getAuxiliarySockets().get("http");
+ }
+
+ protected String makeUrl(String path) {
+ return String.format("http://%s:%s%s", httpServer.getHostName(), httpServer.getPort(), path);
+ }
+
+ protected WebResource.Builder getRequestBuilder(String path) {
+ assertNotNull("HTTP server must be started first", httpServer);
+ ClientConfig config = new DefaultClientConfig();
+ config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
+ config.getClasses().add(GsonMessageBodyHandler.class);
+ Client client = Client.create(config);
+ // Disable redirects so we can unit test them.
+ client.setFollowRedirects(false);
+ return client.resource(makeUrl(path)).getRequestBuilder().accept(MediaType.APPLICATION_JSON);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/test/java/org/apache/aurora/scheduler/http/MnameTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/MnameTest.java b/src/test/java/org/apache/aurora/scheduler/http/MnameTest.java
index b7b6a3e..afa83f2 100644
--- a/src/test/java/org/apache/aurora/scheduler/http/MnameTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/http/MnameTest.java
@@ -36,7 +36,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
-public class MnameTest extends ServletModuleTest {
+public class MnameTest extends JettyServerModuleTest {
private static final String SLAVE_HOST = "fakehost";
private static final int PORT = 50000;
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/test/java/org/apache/aurora/scheduler/http/ServletFilterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/ServletFilterTest.java b/src/test/java/org/apache/aurora/scheduler/http/ServletFilterTest.java
index 0399ec0..2c31df6 100644
--- a/src/test/java/org/apache/aurora/scheduler/http/ServletFilterTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/http/ServletFilterTest.java
@@ -27,7 +27,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
-public class ServletFilterTest extends ServletModuleTest {
+public class ServletFilterTest extends JettyServerModuleTest {
protected ClientResponse get(String path) {
return getRequestBuilder(path)
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/test/java/org/apache/aurora/scheduler/http/ServletModuleTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/ServletModuleTest.java b/src/test/java/org/apache/aurora/scheduler/http/ServletModuleTest.java
deleted file mode 100644
index 63c504e..0000000
--- a/src/test/java/org/apache/aurora/scheduler/http/ServletModuleTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.aurora.scheduler.http;
-
-import java.net.InetSocketAddress;
-
-import javax.ws.rs.core.MediaType;
-
-import com.google.common.base.Throwables;
-import com.google.common.testing.TearDown;
-import com.google.common.util.concurrent.RateLimiter;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.TypeLiteral;
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.WebResource;
-import com.sun.jersey.api.client.config.ClientConfig;
-import com.sun.jersey.api.client.config.DefaultClientConfig;
-import com.sun.jersey.api.json.JSONConfiguration;
-import com.twitter.common.application.ShutdownRegistry.ShutdownRegistryImpl;
-import com.twitter.common.application.StartupRegistry;
-import com.twitter.common.application.modules.HttpModule;
-import com.twitter.common.application.modules.LifecycleModule;
-import com.twitter.common.application.modules.LocalServiceRegistry;
-import com.twitter.common.application.modules.LogModule;
-import com.twitter.common.application.modules.StatsModule;
-import com.twitter.common.base.Command;
-import com.twitter.common.net.pool.DynamicHostSet;
-import com.twitter.common.net.pool.DynamicHostSet.HostChangeMonitor;
-import com.twitter.common.testing.easymock.EasyMockTest;
-import com.twitter.common.util.BackoffStrategy;
-import com.twitter.thrift.ServiceInstance;
-
-import org.apache.aurora.gen.AuroraAdmin;
-import org.apache.aurora.gen.ServerInfo;
-import org.apache.aurora.scheduler.async.OfferQueue;
-import org.apache.aurora.scheduler.async.RescheduleCalculator;
-import org.apache.aurora.scheduler.async.TaskGroups.TaskGroupsSettings;
-import org.apache.aurora.scheduler.async.TaskScheduler;
-import org.apache.aurora.scheduler.cron.CronJobManager;
-import org.apache.aurora.scheduler.http.api.GsonMessageBodyHandler;
-import org.apache.aurora.scheduler.quota.QuotaManager;
-import org.apache.aurora.scheduler.state.LockManager;
-import org.apache.aurora.scheduler.state.SchedulerCore;
-import org.apache.aurora.scheduler.storage.Storage;
-import org.apache.aurora.scheduler.storage.entities.IServerInfo;
-import org.apache.aurora.scheduler.storage.testing.StorageTestUtil;
-import org.easymock.Capture;
-import org.junit.Before;
-
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.expect;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * TODO(wfarner): Break apart ServletModule so test setup isn't so involved.
- * TODO(wfarner): Come up with an approach for these tests that doesn't require starting an actual
- * HTTP server for each test case.
- *
- */
-public abstract class ServletModuleTest extends EasyMockTest {
-
- private Injector injector;
- protected StorageTestUtil storage;
- protected InetSocketAddress httpServer;
- protected Capture<HostChangeMonitor<ServiceInstance>> schedulerWatcher;
- protected AuroraAdmin.Iface thrift;
-
- @Before
- public final void setUpServletModuleTest() throws Exception {
- storage = new StorageTestUtil(this);
- final DynamicHostSet<ServiceInstance> schedulers =
- createMock(new Clazz<DynamicHostSet<ServiceInstance>>() { });
-
- injector = Guice.createInjector(
- new ServletModule(),
- new LogModule(),
- new StatsModule(),
- new HttpModule(),
- new LifecycleModule(),
- new AbstractModule() {
- <T> T bindMock(Class<T> clazz) {
- T mock = createMock(clazz);
- bind(clazz).toInstance(mock);
- return mock;
- }
-
- @Override
- protected void configure() {
- bind(Storage.class).toInstance(storage.storage);
- bind(IServerInfo.class).toInstance(IServerInfo.build(new ServerInfo()
- .setClusterName("unittest")
- .setThriftAPIVersion(100)
- .setStatsUrlPrefix("none")));
- bind(TaskGroupsSettings.class).toInstance(
- new TaskGroupsSettings(bindMock(BackoffStrategy.class), RateLimiter.create(1000)));
-
- bind(new TypeLiteral<DynamicHostSet<ServiceInstance>>() { }).toInstance(schedulers);
- thrift = bindMock(AuroraAdmin.Iface.class);
- bindMock(CronJobManager.class);
- bindMock(LockManager.class);
- bindMock(OfferQueue.class);
- bindMock(QuotaManager.class);
- bindMock(RescheduleCalculator.class);
- bindMock(SchedulerCore.class);
- bindMock(TaskScheduler.class);
- bindMock(Thread.UncaughtExceptionHandler.class);
- }
- });
-
- schedulerWatcher = createCapture();
- expect(schedulers.watch(capture(schedulerWatcher))).andReturn(createMock(Command.class));
- }
-
- protected void replayAndStart() {
- control.replay();
-
- final ShutdownRegistryImpl shutdownRegistry = injector.getInstance(ShutdownRegistryImpl.class);
- addTearDown(new TearDown() {
- @Override
- public void tearDown() {
- shutdownRegistry.execute();
- }
- });
- try {
- injector.getInstance(StartupRegistry.class).execute();
- } catch (Exception e) {
- throw Throwables.propagate(e);
- }
- LocalServiceRegistry serviceRegistry = injector.getInstance(LocalServiceRegistry.class);
- httpServer = serviceRegistry.getAuxiliarySockets().get("http");
- }
-
- protected String makeUrl(String path) {
- return String.format("http://%s:%s%s", httpServer.getHostName(), httpServer.getPort(), path);
- }
-
- protected WebResource.Builder getRequestBuilder(String path) {
- assertNotNull("HTTP server must be started first", httpServer);
- ClientConfig config = new DefaultClientConfig();
- config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
- config.getClasses().add(GsonMessageBodyHandler.class);
- Client client = Client.create(config);
- // Disable redirects so we can unit test them.
- client.setFollowRedirects(false);
- return client.resource(makeUrl(path)).getRequestBuilder().accept(MediaType.APPLICATION_JSON);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/6449b920/src/test/java/org/apache/aurora/scheduler/http/api/ApiBetaTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/api/ApiBetaTest.java b/src/test/java/org/apache/aurora/scheduler/http/api/ApiBetaTest.java
index 0bcc015..ec467aa 100644
--- a/src/test/java/org/apache/aurora/scheduler/http/api/ApiBetaTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/http/api/ApiBetaTest.java
@@ -50,7 +50,7 @@ import org.apache.aurora.gen.SessionKey;
import org.apache.aurora.gen.TaskConfig;
import org.apache.aurora.gen.TaskConstraint;
import org.apache.aurora.gen.TaskQuery;
-import org.apache.aurora.scheduler.http.ServletModuleTest;
+import org.apache.aurora.scheduler.http.JettyServerModuleTest;
import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
import org.junit.Test;
@@ -61,7 +61,7 @@ import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-public class ApiBetaTest extends ServletModuleTest {
+public class ApiBetaTest extends JettyServerModuleTest {
private static final ITaskConfig TASK_CONFIG = ITaskConfig.build(
new TaskConfig()