package com.etsy.statsd.profiler.server; import com.etsy.statsd.profiler.Profiler; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import org.vertx.java.core.Handler; import org.vertx.java.core.http.HttpServerRequest; import org.vertx.java.core.http.RouteMatcher; import java.util.*; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicReference; /** * Handler for HTTP requests to the profiler server * * @author Andrew Johnson */ public final class RequestHandler { private RequestHandler() { } /** * Construct a RouteMatcher for the supported routes * * @param activeProfilers The active profilers * @return A RouteMatcher that matches all supported routes */ public static RouteMatcher getMatcher(final Map<String, ScheduledFuture<?>> runningProfilers, Map<String, Profiler> activeProfilers, AtomicReference<Boolean> isRunning, List<String> errors) { RouteMatcher matcher = new RouteMatcher(); matcher.get("/profilers", RequestHandler.handleGetProfilers(runningProfilers)); matcher.get("/disable/:profiler", RequestHandler.handleDisableProfiler(runningProfilers)); matcher.get("/status/profiler/:profiler", RequestHandler.handleProfilerStatus(activeProfilers)); matcher.get("/errors", RequestHandler.handleErrorMessages(errors)); matcher.get("/isRunning", RequestHandler.isRunning(isRunning)); return matcher; } /** * Handle a GET to /isRunning * * @return A Handler that returns all running profilers */ public static Handler<HttpServerRequest> isRunning(final AtomicReference<Boolean> isRunning) { return new Handler<HttpServerRequest>() { @Override public void handle(HttpServerRequest httpServerRequest) { httpServerRequest.response().end(String.format("isRunning: %b", isRunning.get())); } }; } /** * Handle a GET to /profilers * * @return A Handler that handles a request to the /profilers endpoint */ public static Handler<HttpServerRequest> handleGetProfilers(final Map<String, ScheduledFuture<?>> runningProfilers) { return new Handler<HttpServerRequest>() { @Override public void handle(HttpServerRequest httpServerRequest) { httpServerRequest.response().end(Joiner.on("\n").join(getEnabledProfilers(runningProfilers))); } }; } /** * Handle a GET to /errors * * @return The last 10 error stacktraces */ public static Handler<HttpServerRequest> handleErrorMessages(final List<String> errors) { return new Handler<HttpServerRequest>() { @Override public void handle(HttpServerRequest httpServerRequest) { httpServerRequest.response().end("Errors: " + Joiner.on("\n").join(errors)); } }; } /** * Handle a GET to /disable/:profiler * * @param activeProfilers The active profilers * @return A Handler that handles a request to the /disable/:profiler endpoint */ public static Handler<HttpServerRequest> handleDisableProfiler(final Map<String, ScheduledFuture<?>> activeProfilers) { return new Handler<HttpServerRequest>() { @Override public void handle(HttpServerRequest httpServerRequest) { String profilerToDisable = httpServerRequest.params().get("profiler"); ScheduledFuture<?> future = activeProfilers.get(profilerToDisable); future.cancel(false); httpServerRequest.response().end(String.format("Disabled profiler %s", profilerToDisable)); } }; } /** * Handle a GET to /status/profiler/:profiler * * @param activeProfilers The active profilers * @return A Handler that handles a request to the /disable/:profiler endpoint */ public static Handler<HttpServerRequest> handleProfilerStatus(final Map<String, Profiler> activeProfilers) { return new Handler<HttpServerRequest>() { @Override public void handle(HttpServerRequest httpServerRequest) { String profilerName = httpServerRequest.params().get("profiler"); Profiler profiler = activeProfilers.get(profilerName); httpServerRequest.response().end(String.format("Recorded stats %d\n", profiler.getRecordedStats())); } }; } /** * Get all enabled profilers * @param activeProfilers The active profilers * @return A sorted List<String> containing the names of profilers that are currently running */ private static List<String> getEnabledProfilers(final Map<String, ScheduledFuture<?>> activeProfilers) { Collection<String> profilers = Collections2.transform(Collections2.filter(activeProfilers.entrySet(), new Predicate<Map.Entry<String, ScheduledFuture<?>>>() { @Override public boolean apply(Map.Entry<String, ScheduledFuture<?>> input) { return !input.getValue().isDone(); } }), new Function<Map.Entry<String, ScheduledFuture<?>>, String>() { @Override public String apply(Map.Entry<String, ScheduledFuture<?>> input) { return input.getKey(); } }); List<String> result = new ArrayList<>(profilers); Collections.sort(result); return result; } }