From 53c7222107d749e55a4d511593cffc876081edae Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Thu, 5 May 2016 16:38:50 -0400 Subject: [PATCH] More stuff --- apiv3.properties | 7 +++- src/main/java/net/frozenorb/apiv3/APIv3.java | 42 ++++++++++++++++--- .../frozenorb/apiv3/auditLog/AuditLog.java | 1 - .../apiv3/filters/ActorAttributeFilter.java | 5 ++- .../apiv3/filters/AuthorizationFilter.java | 3 +- .../apiv3/filters/MetricsAfterFilter.java | 25 +++++++++++ .../apiv3/filters/MetricsBeforeFilter.java | 24 +++++++++++ .../net/frozenorb/apiv3/models/Grant.java | 2 - .../frozenorb/apiv3/models/ServerGroup.java | 1 - .../frozenorb/apiv3/models/UserMetaEntry.java | 1 - .../chatFilterList/GETChatFilterList.java | 5 --- .../apiv3/routes/grants/DELETEGrant.java | 1 - .../routes/punishments/DELETEPunishment.java | 1 - .../routes/punishments/POSTUserPunish.java | 1 - .../routes/users/GETUserVerifyPassword.java | 4 -- .../unsorted/LoggingExceptionHandler.java | 7 ++++ .../apiv3/unsorted/Notification.java | 2 - 17 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java create mode 100644 src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java diff --git a/apiv3.properties b/apiv3.properties index 126d564..0254f7c 100644 --- a/apiv3.properties +++ b/apiv3.properties @@ -1,3 +1,5 @@ +general.id=main +general.releaseStage=production mongo.address=ds055505.mongolab.com mongo.port=55505 mongo.database=minehqapi @@ -5,11 +7,14 @@ mongo.username=test mongo.password=test redis.address=localhost redis.port=6379 -http.address= +http.address=0.0.0.0 http.port=80 http.workerThreads=6 twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740 twillio.authToken=982592505a171d3be6b0722f5ecacc0e mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ +librato.email=cmcdonald.main@gmail.com +librato.apiKey=a818c3eca8a59d6d9cf76dc9f0d237c6aa97f257c482ce3363cf55a5431bc153 +bugsnag.apiKey= auth.permittedUserRanks=developer,owner auth.websiteApiKey=RVbp4hY6sCFVaf \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index fa9a72c..bf3a36a 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -1,17 +1,17 @@ package net.frozenorb.apiv3; +import com.bugsnag.Client; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.librato.metrics.LibratoReporter; import com.mongodb.MongoClient; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; import lombok.Getter; -import net.frozenorb.apiv3.filters.ActorAttributeFilter; -import net.frozenorb.apiv3.filters.AuthorizationFilter; -import net.frozenorb.apiv3.filters.ContentTypeFilter; -import net.frozenorb.apiv3.models.NotificationTemplate; +import net.frozenorb.apiv3.filters.*; import net.frozenorb.apiv3.routes.GETDump; import net.frozenorb.apiv3.routes.GETWhoAmI; import net.frozenorb.apiv3.routes.NotFound; @@ -41,7 +41,6 @@ import net.frozenorb.apiv3.routes.users.*; import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter; import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler; -import net.frozenorb.apiv3.unsorted.Notification; import org.bson.types.ObjectId; import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Morphia; @@ -52,6 +51,7 @@ import redis.clients.jedis.JedisPool; import java.io.FileInputStream; import java.io.InputStream; import java.util.Properties; +import java.util.concurrent.TimeUnit; import static spark.Spark.*; @@ -60,6 +60,8 @@ public final class APIv3 { @Getter private static Datastore datastore; @Getter private static Properties config = new Properties(); @Getter private static JedisPool redisPool; + @Getter private static MetricRegistry metrics = new MetricRegistry(); + @Getter private static Client bugsnagClient; @Getter private static final Gson gson = new GsonBuilder() .registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy()) @@ -71,6 +73,8 @@ public final class APIv3 { setupConfig(); setupDatabase(); setupRedis(); + setupMetrics(); + setupBugsnag(); setupHttp(); } @@ -110,6 +114,30 @@ public final class APIv3 { ); } + private void setupMetrics() { + Gauge memoryUsageGauge = () -> Runtime.getRuntime().totalMemory() / (1024 * 1024); + Gauge memoryMaxGauge = () -> Runtime.getRuntime().maxMemory() / (1024 * 1024); + + metrics.register(MetricRegistry.name("apiv3", "memory", "usage"), memoryUsageGauge); + metrics.register(MetricRegistry.name("apiv3", "memory", "max"), memoryMaxGauge); + + LibratoReporter.enable( + LibratoReporter.builder( + metrics, + config.getProperty("librato.email"), + config.getProperty("librato.apiKey"), + config.getProperty("general.id") + ), + 1, + TimeUnit.MINUTES + ); + } + + private void setupBugsnag() { + bugsnagClient = new Client(config.getProperty("bugsnag.apiKey")); + bugsnagClient.setReleaseStage(config.getProperty("general.releaseStage")); + } + private void setupHttp() { ipAddress(config.getProperty("http.address")); port(Integer.parseInt(config.getProperty("http.port"))); @@ -117,6 +145,8 @@ public final class APIv3 { before(new ContentTypeFilter()); before(new ActorAttributeFilter()); before(new AuthorizationFilter()); + before(new MetricsBeforeFilter()); + after(new MetricsAfterFilter()); exception(Exception.class, new LoggingExceptionHandler()); // TODO: The commented out routes diff --git a/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java b/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java index f246540..e668c9f 100644 --- a/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java @@ -6,7 +6,6 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.models.AuditLogEntry; import net.frozenorb.apiv3.models.User; -import org.bson.Document; import java.util.Map; diff --git a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java b/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java index 86f0712..4057f14 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java @@ -18,7 +18,7 @@ public final class ActorAttributeFilter implements Filter { String mhqAuthHeader = req.headers("MHQ-Authorization"); if (authHeader != null) { - req.attribute("actor", processBasicAuthorization(authHeader)); + req.attribute("actor", processBasicAuthorization(authHeader, res)); } else if (mhqAuthHeader != null) { req.attribute("actor", processMHQAuthorization(mhqAuthHeader)); } else { @@ -27,7 +27,7 @@ public final class ActorAttributeFilter implements Filter { } @SuppressWarnings("deprecation") // We purposely get the User by their last username. - private Actor processBasicAuthorization(String authHeader) { + private Actor processBasicAuthorization(String authHeader, Response res) { String encodedHeader = authHeader.substring("Basic ".length()); String[] credentials = Base64.base64Decode(encodedHeader).split(":"); @@ -40,6 +40,7 @@ public final class ActorAttributeFilter implements Filter { } } + res.header("WWW-Authenticate", "Basic realm=\"MineHQ\""); Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Failed to authorize."))); return null; } diff --git a/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java b/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java index a20a17b..44f006f 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java @@ -14,7 +14,8 @@ public final class AuthorizationFilter implements Filter { Actor actor = req.attribute("actor"); if (!actor.isAuthorized()) { - Spark.halt(APIv3.getGson().toJson(ErrorUtils.error("Unauthorized access: Please authenticate as either a server, the website, or an authorized user. You're currently authorized as " + actor.getName()))); + res.header("WWW-Authenticate", "Basic realm=\"MineHQ\""); + Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Unauthorized access: Please authenticate as either a server, the website, or an authorized user. You're currently authorized as " + actor.getName()))); } } diff --git a/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java b/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java new file mode 100644 index 0000000..cda9b9d --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java @@ -0,0 +1,25 @@ +package net.frozenorb.apiv3.filters; + +import com.codahale.metrics.Histogram; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import net.frozenorb.apiv3.APIv3; +import spark.Filter; +import spark.Request; +import spark.Response; + +public final class MetricsAfterFilter implements Filter { + + private Histogram responseLengthMetric = APIv3.getMetrics().histogram(MetricRegistry.name("apiv3", "http", "responseLength")); + + public void handle(Request req, Response res) { + responseLengthMetric.update(req.contentLength()); + + Timer.Context timerMetricActorType = req.attribute("timerMetric.actorType"); + Timer.Context timerMetricGlobal = req.attribute("timerMetric.global"); + + timerMetricActorType.stop(); + timerMetricGlobal.stop(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java b/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java new file mode 100644 index 0000000..f2cf8ad --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java @@ -0,0 +1,24 @@ +package net.frozenorb.apiv3.filters; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.actors.Actor; +import spark.Filter; +import spark.Request; +import spark.Response; + +public final class MetricsBeforeFilter implements Filter { + + private Timer responseTimesGlobalMetric = APIv3.getMetrics().timer(MetricRegistry.name(getClass(), "responseTimes", "global")); + + public void handle(Request req, Response res) { + Actor actor = req.attribute("actor"); + String metricName = MetricRegistry.name(getClass(), "responseTimes", actor.getType().name()); + Timer responseTimesActorTypeMetric = APIv3.getMetrics().timer(metricName); + + req.attribute("timerMetric.global", responseTimesGlobalMetric.time()); + req.attribute("timerMetric.actorType", responseTimesActorTypeMetric.time()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/Grant.java b/src/main/java/net/frozenorb/apiv3/models/Grant.java index 3db6e92..62ba897 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/models/Grant.java @@ -3,8 +3,6 @@ package net.frozenorb.apiv3.models; import com.google.common.collect.Collections2; import lombok.Getter; import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.actors.Actor; -import net.frozenorb.apiv3.actors.ActorType; import org.bson.types.ObjectId; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; diff --git a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java index fa3c7dc..56c4b50 100644 --- a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java @@ -1,6 +1,5 @@ package net.frozenorb.apiv3.models; -import com.google.common.collect.ImmutableSet; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; diff --git a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java index 48dfe79..319fc51 100644 --- a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMap; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; -import org.bson.Document; import org.bson.types.ObjectId; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; diff --git a/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java b/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java index eaad9f0..0a644ce 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java +++ b/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java @@ -1,11 +1,6 @@ package net.frozenorb.apiv3.routes.chatFilterList; import com.google.common.collect.ImmutableSet; -import net.frozenorb.apiv3.actors.Actor; -import net.frozenorb.apiv3.actors.ActorType; -import net.frozenorb.apiv3.models.Server; -import net.frozenorb.apiv3.models.ServerGroup; -import net.frozenorb.apiv3.utils.ErrorUtils; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java index 4255feb..1208241 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -7,7 +7,6 @@ import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.utils.ErrorUtils; -import org.bson.Document; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java index 4a8dc95..97548da 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -7,7 +7,6 @@ import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.utils.ErrorUtils; -import org.bson.Document; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java index 083a0a5..23f4788 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -2,7 +2,6 @@ package net.frozenorb.apiv3.routes.punishments; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.Punishment; -import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.utils.ErrorUtils; diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java index 8cc4698..1f959d5 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java @@ -3,14 +3,10 @@ package net.frozenorb.apiv3.routes.users; import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.utils.ErrorUtils; -import net.frozenorb.apiv3.utils.IPUtils; -import net.frozenorb.apiv3.utils.TOTPUtils; import spark.Request; import spark.Response; import spark.Route; -import java.util.concurrent.TimeUnit; - public final class GETUserVerifyPassword implements Route { public Object handle(Request req, Response res) { diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java b/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java index 645fee5..2c51c5d 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java @@ -1,5 +1,6 @@ package net.frozenorb.apiv3.unsorted; +import com.codahale.metrics.Timer; import lombok.extern.slf4j.Slf4j; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -12,6 +13,12 @@ import spark.Response; public final class LoggingExceptionHandler implements ExceptionHandler { public void handle(Exception ex, Request req, Response res) { + Timer.Context timerMetricActorType = req.attribute("timerMetric.actorType"); + Timer.Context timerMetricGlobal = req.attribute("timerMetric.global"); + + timerMetricActorType.stop(); + timerMetricGlobal.stop(); + String code = new ObjectId().toHexString(); log.error(code + ":", ex); diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java index bfea3c9..5a74903 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java @@ -7,8 +7,6 @@ import com.cribbstechnologies.clients.mandrill.model.MandrillRecipient; import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest; import com.twilio.sdk.TwilioRestException; import com.twilio.sdk.resource.factory.MessageFactory; -import com.twilio.sdk.resource.instance.Message; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.NotificationTemplate; import net.frozenorb.apiv3.utils.MandrillUtils; import net.frozenorb.apiv3.utils.TwillioUtils;