diff options
Diffstat (limited to 'src/com/p4square')
-rw-r--r-- | src/com/p4square/f1oauth/F1Access.java | 279 | ||||
-rw-r--r-- | src/com/p4square/grow/GrowProcessComponent.java | 20 | ||||
-rw-r--r-- | src/com/p4square/grow/backend/GrowBackend.java | 18 | ||||
-rw-r--r-- | src/com/p4square/grow/frontend/GrowFrontend.java | 21 | ||||
-rw-r--r-- | src/com/p4square/restlet/metrics/MetricRouter.java | 61 | ||||
-rw-r--r-- | src/com/p4square/restlet/metrics/MetricsApplication.java | 43 | ||||
-rw-r--r-- | src/com/p4square/restlet/metrics/MetricsResource.java | 32 |
7 files changed, 401 insertions, 73 deletions
diff --git a/src/com/p4square/f1oauth/F1Access.java b/src/com/p4square/f1oauth/F1Access.java index 5b6f7ce..c3307f1 100644 --- a/src/com/p4square/f1oauth/F1Access.java +++ b/src/com/p4square/f1oauth/F1Access.java @@ -12,6 +12,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.codahale.metrics.Counter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; + import org.apache.log4j.Logger; import org.restlet.Context; @@ -60,6 +64,8 @@ public class F1Access { private final Map<String, String> mAttributeIdByName; + private MetricRegistry mMetricRegistry; + /** */ public F1Access(Context context, String consumerKey, String consumerSecret, @@ -108,19 +114,46 @@ public class F1Access { } /** + * Set the MetricRegistry to get metrics recorded. + */ + public void setMetricRegistry(MetricRegistry metrics) { + mMetricRegistry = metrics; + } + + /** * Request an AccessToken for a particular username and password. * * This is an F1 extension to OAuth: * http://developer.fellowshipone.com/docs/v1/Util/AuthDocs.help#2creds */ public OAuthUser getAccessToken(String username, String password) throws OAuthException { - Request request = new Request(Method.POST, mBaseUrl + mMethod + TRUSTED_ACCESSTOKEN_URL); - request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); + Timer.Context timer = getTimer("F1Access.getAccessToken.time"); + boolean success = true; + + try { + Request request = new Request(Method.POST, + mBaseUrl + mMethod + TRUSTED_ACCESSTOKEN_URL); + request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); + + String base64String = Base64.encode((username + " " + password).getBytes(), false); + request.setEntity(new StringRepresentation(base64String)); - String base64String = Base64.encode((username + " " + password).getBytes(), false); - request.setEntity(new StringRepresentation(base64String)); + return mOAuthHelper.processAccessTokenRequest(request); - return mOAuthHelper.processAccessTokenRequest(request); + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getAccessToken.success"); + } else { + incrementCounter("F1Access.getAccessToken.failure"); + } + } } /** @@ -135,26 +168,45 @@ public class F1Access { */ public boolean createAccount(String firstname, String lastname, String email, String redirect) throws OAuthException { - String req = String.format("{\n\"account\":{\n\"firstName\":\"%s\",\n" - + "\"lastName\":\"%s\",\n\"email\":\"%s\",\n" - + "\"urlRedirect\":\"%s\"\n}\n}", - firstname, lastname, email, redirect); + Timer.Context timer = getTimer("F1Access.createAccount.time"); + boolean success = true; - Request request = new Request(Method.POST, mBaseUrl + "Accounts"); - request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); - request.setEntity(new StringRepresentation(req, MediaType.APPLICATION_JSON)); + try { + String req = String.format("{\n\"account\":{\n\"firstName\":\"%s\",\n" + + "\"lastName\":\"%s\",\n\"email\":\"%s\",\n" + + "\"urlRedirect\":\"%s\"\n}\n}", + firstname, lastname, email, redirect); - Response response = mOAuthHelper.getResponse(request); + Request request = new Request(Method.POST, mBaseUrl + "Accounts"); + request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); + request.setEntity(new StringRepresentation(req, MediaType.APPLICATION_JSON)); - Status status = response.getStatus(); - if (Status.SUCCESS_NO_CONTENT.equals(status)) { - return true; + Response response = mOAuthHelper.getResponse(request); - } else if (Status.CLIENT_ERROR_CONFLICT.equals(status)) { - return false; + Status status = response.getStatus(); + if (Status.SUCCESS_NO_CONTENT.equals(status)) { + return true; - } else { - throw new OAuthException(status); + } else if (Status.CLIENT_ERROR_CONFLICT.equals(status)) { + return false; + + } else { + throw new OAuthException(status); + } + + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.createAccount.success"); + } else { + incrementCounter("F1Access.createAccount.failure"); + } } } @@ -180,24 +232,44 @@ public class F1Access { */ @Override public F1User getF1User(OAuthUser user) throws OAuthException, IOException { - Request request = new Request(Method.GET, user.getLocation() + ".json"); - request.setChallengeResponse(mUser.getChallengeResponse()); - Response response = mOAuthHelper.getResponse(request); + Timer.Context timer = getTimer("F1Access.getF1User.time"); + boolean success = true; try { - Status status = response.getStatus(); - if (status.isSuccess()) { - JacksonRepresentation<Map> entity = - new JacksonRepresentation<Map>(response.getEntity(), Map.class); - Map data = entity.getObject(); - return new F1User(user, data); + Request request = new Request(Method.GET, user.getLocation() + ".json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); - } else { - throw new OAuthException(status); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + Map data = entity.getObject(); + return new F1User(user, data); + + } else { + throw new OAuthException(status); + } + + } finally { + if (response.getEntity() != null) { + response.release(); + } } + + } catch (Exception e) { + success = false; + throw e; + } finally { - if (response.getEntity() != null) { - response.release(); + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getF1User.success"); + } else { + incrementCounter("F1Access.getF1User.failure"); } } } @@ -207,42 +279,61 @@ public class F1Access { // Note: this list is shared by all F1 users. synchronized (mAttributeIdByName) { if (mAttributeIdByName.size() == 0) { - // Reload attributes. Maybe it will be there now... - Request request = new Request(Method.GET, - mBaseUrl + "People/AttributeGroups.json"); - request.setChallengeResponse(mUser.getChallengeResponse()); - Response response = mOAuthHelper.getResponse(request); + Timer.Context timer = getTimer("F1Access.getAttributeList.time"); + boolean success = true; - Representation representation = response.getEntity(); try { - Status status = response.getStatus(); - if (status.isSuccess()) { - JacksonRepresentation<Map> entity = - new JacksonRepresentation<Map>(response.getEntity(), Map.class); - - Map attributeGroups = (Map) entity.getObject().get("attributeGroups"); - List<Map> groups = (List<Map>) attributeGroups.get("attributeGroup"); - - for (Map group : groups) { - List<Map> attributes = (List<Map>) group.get("attribute"); - if (attributes != null) { - for (Map attribute : attributes) { - String id = (String) attribute.get("@id"); - String name = ((String) attribute.get("name")); - mAttributeIdByName.put(name.toLowerCase(), id); - LOG.debug("Caching attribute '" + name - + "' with id '" + id + "'"); + // Reload attributes. Maybe it will be there now... + Request request = new Request(Method.GET, + mBaseUrl + "People/AttributeGroups.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + + Map attributeGroups = (Map) entity.getObject().get("attributeGroups"); + List<Map> groups = (List<Map>) attributeGroups.get("attributeGroup"); + + for (Map group : groups) { + List<Map> attributes = (List<Map>) group.get("attribute"); + if (attributes != null) { + for (Map attribute : attributes) { + String id = (String) attribute.get("@id"); + String name = ((String) attribute.get("name")); + mAttributeIdByName.put(name.toLowerCase(), id); + LOG.debug("Caching attribute '" + name + + "' with id '" + id + "'"); + } } } } + + } catch (IOException e) { + throw new F1Exception("Could not parse AttributeGroups.", e); + + } finally { + if (representation != null) { + representation.release(); + } } - } catch (IOException e) { - throw new F1Exception("Could not parse AttributeGroups.", e); + } catch (Exception e) { + success = false; + throw e; } finally { - if (representation != null) { - representation.release(); + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getAttributeList.success"); + } else { + incrementCounter("F1Access.getAttributeList.failure"); } } } @@ -270,7 +361,10 @@ public class F1Access { // Get Attribute Template Map attributeTemplate = null; - { + Timer.Context timer = getTimer("F1Access.addAttribute.GET.time"); + boolean success = true; + + try { Request request = new Request(Method.GET, mBaseUrl + "People/" + userId + "/Attributes/new.json"); request.setChallengeResponse(mUser.getChallengeResponse()); @@ -297,6 +391,19 @@ public class F1Access { representation.release(); } } + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.addAttribute.GET.success"); + } else { + incrementCounter("F1Access.addAttribute.GET.failure"); + } } if (attributeTemplate == null) { @@ -323,7 +430,10 @@ public class F1Access { // POST new attribute Status status; - { + timer = getTimer("F1Access.addAttribute.POST.time"); + success = true; + + try { Request request = new Request(Method.POST, mBaseUrl + "People/" + userId + "/Attributes.json"); request.setChallengeResponse(mUser.getChallengeResponse()); @@ -343,6 +453,19 @@ public class F1Access { representation.release(); } } + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.addAttribute.POST.success"); + } else { + incrementCounter("F1Access.getAccessToken.POST.failure"); + } } LOG.debug("addAttribute failed POST: " + status); @@ -356,7 +479,10 @@ public class F1Access { Map attributesResponse; // Get Attributes - { + Timer.Context timer = getTimer("F1Access.getAttribute.time"); + boolean success = true; + + try { Request request = new Request(Method.GET, mBaseUrl + "People/" + userId + "/Attributes.json"); request.setChallengeResponse(mUser.getChallengeResponse()); @@ -383,6 +509,19 @@ public class F1Access { representation.release(); } } + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getAttribute.success"); + } else { + incrementCounter("F1Access.getAttribute.failure"); + } } // Parse Response @@ -438,4 +577,18 @@ public class F1Access { } } + + private Timer.Context getTimer(String name) { + if (mMetricRegistry != null) { + return mMetricRegistry.timer(name).time(); + } else { + return null; + } + } + + private void incrementCounter(String name) { + if (mMetricRegistry != null) { + mMetricRegistry.counter(name).inc(); + } + } } diff --git a/src/com/p4square/grow/GrowProcessComponent.java b/src/com/p4square/grow/GrowProcessComponent.java index 791f177..f63538c 100644 --- a/src/com/p4square/grow/GrowProcessComponent.java +++ b/src/com/p4square/grow/GrowProcessComponent.java @@ -6,6 +6,10 @@ package com.p4square.grow; import java.io.File; import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.ConsoleReporter; +import com.codahale.metrics.MetricRegistry; import org.apache.log4j.Logger; @@ -21,6 +25,7 @@ import com.p4square.grow.backend.BackendVerifier; import com.p4square.grow.backend.GrowBackend; import com.p4square.grow.config.Config; import com.p4square.grow.frontend.GrowFrontend; +import com.p4square.restlet.metrics.MetricsApplication; /** * @@ -32,6 +37,7 @@ public class GrowProcessComponent extends Component { private static final String BACKEND_REALM = "Grow Backend"; private final Config mConfig; + private final MetricRegistry mMetricRegistry; /** * Create a new Grow Process website component combining a frontend and backend. @@ -50,12 +56,15 @@ public class GrowProcessComponent extends Component { mConfig = config; mConfig.updateConfig(this.getClass().getResourceAsStream("/grow.properties")); + // Prepare Metrics + mMetricRegistry = new MetricRegistry(); + // Frontend - GrowFrontend frontend = new GrowFrontend(mConfig); + GrowFrontend frontend = new GrowFrontend(mConfig, mMetricRegistry); getDefaultHost().attach(frontend); // Backend - GrowBackend backend = new GrowBackend(mConfig); + GrowBackend backend = new GrowBackend(mConfig, mMetricRegistry); getInternalRouter().attach("/backend", backend); // Authenticated access to the backend @@ -64,6 +73,13 @@ public class GrowProcessComponent extends Component { false, ChallengeScheme.HTTP_BASIC, BACKEND_REALM, verifier); auth.setNext(backend); getDefaultHost().attach("/backend", auth); + + // Authenticated access to metrics + ChallengeAuthenticator metricAuth = new ChallengeAuthenticator( + getContext().createChildContext(), false, + ChallengeScheme.HTTP_BASIC, BACKEND_REALM, verifier); + metricAuth.setNext(new MetricsApplication(mMetricRegistry)); + getDefaultHost().attach("/metrics", metricAuth); } diff --git a/src/com/p4square/grow/backend/GrowBackend.java b/src/com/p4square/grow/backend/GrowBackend.java index e73ad38..4091138 100644 --- a/src/com/p4square/grow/backend/GrowBackend.java +++ b/src/com/p4square/grow/backend/GrowBackend.java @@ -6,6 +6,8 @@ package com.p4square.grow.backend; import java.io.IOException; +import com.codahale.metrics.MetricRegistry; + import org.apache.log4j.Logger; import org.restlet.Application; @@ -42,6 +44,8 @@ import com.p4square.grow.backend.feed.FeedDataProvider; import com.p4square.grow.backend.feed.ThreadResource; import com.p4square.grow.backend.feed.TopicResource; +import com.p4square.restlet.metrics.MetricRouter; + /** * Main class for the backend application. * @@ -51,22 +55,30 @@ public class GrowBackend extends Application implements GrowData { private final static Logger LOG = Logger.getLogger(GrowBackend.class); + private final MetricRegistry mMetricRegistry; + private final Config mConfig; private final GrowData mGrowData; public GrowBackend() { - this(new Config()); + this(new Config(), new MetricRegistry()); } - public GrowBackend(Config config) { + public GrowBackend(Config config, MetricRegistry metricRegistry) { mConfig = config; + mMetricRegistry = metricRegistry; + mGrowData = new DynamoGrowData(config); } + public MetricRegistry getMetrics() { + return mMetricRegistry; + } + @Override public Restlet createInboundRoot() { - Router router = new Router(getContext()); + Router router = new MetricRouter(getContext(), mMetricRegistry); // Account API router.attach("/accounts/{userId}", AccountResource.class); diff --git a/src/com/p4square/grow/frontend/GrowFrontend.java b/src/com/p4square/grow/frontend/GrowFrontend.java index 926670b..37d4984 100644 --- a/src/com/p4square/grow/frontend/GrowFrontend.java +++ b/src/com/p4square/grow/frontend/GrowFrontend.java @@ -23,6 +23,8 @@ import org.restlet.routing.Redirector; import org.restlet.routing.Router; import org.restlet.security.Authenticator; +import com.codahale.metrics.MetricRegistry; + import org.apache.log4j.Logger; import com.p4square.fmfacade.FMFacade; @@ -33,6 +35,8 @@ import com.p4square.grow.config.Config; import com.p4square.f1oauth.F1Access; import com.p4square.f1oauth.SecondPartyVerifier; +import com.p4square.restlet.metrics.MetricRouter; + import com.p4square.session.SessionCheckingAuthenticator; import com.p4square.session.SessionCreatingAuthenticator; @@ -47,22 +51,28 @@ import com.p4square.session.SessionCreatingAuthenticator; public class GrowFrontend extends FMFacade { private static Logger LOG = Logger.getLogger(GrowFrontend.class); - private Config mConfig; + private final Config mConfig; + private final MetricRegistry mMetricRegistry; private F1Access mHelper; public GrowFrontend() { - this(new Config()); + this(new Config(), new MetricRegistry()); } - public GrowFrontend(Config config) { + public GrowFrontend(Config config, MetricRegistry metricRegistry) { mConfig = config; + mMetricRegistry = metricRegistry; } public Config getConfig() { return mConfig; } + public MetricRegistry getMetrics() { + return mMetricRegistry; + } + @Override public synchronized void start() throws Exception { Template errorTemplate = getTemplate("templates/error.ftl"); @@ -80,6 +90,7 @@ public class GrowFrontend extends FMFacade { mConfig.getString("f1BaseUrl", "staging.fellowshiponeapi.com"), mConfig.getString("f1ChurchCode", "pfseawa"), F1Access.UserType.WEBLINK); + mHelper.setMetricRegistry(mMetricRegistry); } return mHelper; @@ -87,7 +98,7 @@ public class GrowFrontend extends FMFacade { @Override protected Router createRouter() { - Router router = new Router(getContext()); + Router router = new MetricRouter(getContext(), mMetricRegistry); final Authenticator defaultGuard = new SessionCheckingAuthenticator(getContext(), true); defaultGuard.setNext(FreeMarkerPageResource.class); @@ -97,7 +108,7 @@ public class GrowFrontend extends FMFacade { router.attach("/newaccount.html", NewAccountResource.class); router.attach("/newbeliever", NewBelieverResource.class); - final Router accountRouter = new Router(getContext()); + final Router accountRouter = new MetricRouter(getContext(), mMetricRegistry); accountRouter.attach("/authenticate", AuthenticatedResource.class); accountRouter.attach("/logout", LogoutResource.class); diff --git a/src/com/p4square/restlet/metrics/MetricRouter.java b/src/com/p4square/restlet/metrics/MetricRouter.java new file mode 100644 index 0000000..d4da270 --- /dev/null +++ b/src/com/p4square/restlet/metrics/MetricRouter.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.restlet.metrics; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.routing.TemplateRoute; +import org.restlet.routing.Router; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class MetricRouter extends Router { + + private final MetricRegistry mMetricRegistry; + + public MetricRouter(Context context, MetricRegistry metrics) { + super(context); + mMetricRegistry = metrics; + } + + @Override + protected void doHandle(Restlet next, Request request, Response response) { + String baseName; + if (next instanceof TemplateRoute) { + TemplateRoute temp = (TemplateRoute) next; + baseName = MetricRegistry.name("MetricRouter", temp.getTemplate().getPattern()); + } else { + baseName = MetricRegistry.name("MetricRouter", "unknown"); + } + + final Timer.Context aggTimer = mMetricRegistry.timer("MetricRouter.time").time(); + final Timer.Context timer = mMetricRegistry.timer(baseName + ".time").time(); + + try { + super.doHandle(next, request, response); + } finally { + timer.stop(); + aggTimer.stop(); + + // Record status code + boolean success = !response.getStatus().isError(); + if (success) { + mMetricRegistry.counter("MetricRouter.success").inc(); + mMetricRegistry.counter(baseName + ".response.success").inc(); + } else { + mMetricRegistry.counter("MetricRouter.failure").inc(); + mMetricRegistry.counter(baseName + ".response.failure").inc(); + } + } + } +} diff --git a/src/com/p4square/restlet/metrics/MetricsApplication.java b/src/com/p4square/restlet/metrics/MetricsApplication.java new file mode 100644 index 0000000..6caf742 --- /dev/null +++ b/src/com/p4square/restlet/metrics/MetricsApplication.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.restlet.metrics; + +import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.json.MetricsModule; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.restlet.Application; +import org.restlet.Restlet; +import org.restlet.resource.Finder; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class MetricsApplication extends Application { + static final ObjectMapper MAPPER; + static { + MAPPER = new ObjectMapper(); + MAPPER.registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true)); + } + + private final MetricRegistry mMetricRegistry; + + public MetricsApplication(MetricRegistry metrics) { + mMetricRegistry = metrics; + } + + public MetricRegistry getMetricRegistry() { + return mMetricRegistry; + } + + @Override + public Restlet createInboundRoot() { + return new Finder(getContext(), MetricsResource.class); + } +} diff --git a/src/com/p4square/restlet/metrics/MetricsResource.java b/src/com/p4square/restlet/metrics/MetricsResource.java new file mode 100644 index 0000000..e2ab14d --- /dev/null +++ b/src/com/p4square/restlet/metrics/MetricsResource.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.restlet.metrics; + +import com.codahale.metrics.MetricRegistry; + +import org.restlet.ext.jackson.JacksonRepresentation; +import org.restlet.representation.Representation; +import org.restlet.resource.ServerResource; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class MetricsResource extends ServerResource { + + private MetricRegistry mMetricRegistry; + + @Override + public void doInit() { + mMetricRegistry = ((MetricsApplication) getApplication()).getMetricRegistry(); + } + + @Override + protected Representation get() { + JacksonRepresentation<MetricRegistry> rep = new JacksonRepresentation<>(mMetricRegistry); + rep.setObjectMapper(MetricsApplication.MAPPER); + return rep; + } +} |