Java tutorial
/** * Copyright 2014 Groupon.com * * 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 com.arpnetworking.metrics.proxy.models.protocol.v1; import com.arpnetworking.commons.jackson.databind.ObjectMapperFactory; import com.arpnetworking.logback.annotations.LogValue; import com.arpnetworking.metrics.Metrics; import com.arpnetworking.metrics.proxy.actors.Connection; import com.arpnetworking.metrics.proxy.models.messages.Command; import com.arpnetworking.metrics.proxy.models.messages.MetricReport; import com.arpnetworking.metrics.proxy.models.messages.MetricsList; import com.arpnetworking.metrics.proxy.models.messages.MetricsListRequest; import com.arpnetworking.metrics.proxy.models.messages.NewMetric; import com.arpnetworking.metrics.proxy.models.protocol.MessagesProcessor; import com.arpnetworking.steno.LogValueMapFactory; import com.arpnetworking.steno.Logger; import com.arpnetworking.steno.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.Map; import java.util.Set; /** * Processes metrics-based messages. * * @author Brandon Arp (brandonarp at gmail dot com) */ public class MetricMessagesProcessor implements MessagesProcessor { /** * Public constructor. * * @param connection ConnectionContext where processing takes place */ public MetricMessagesProcessor(final Connection connection) { _connection = connection; } /** * {@inheritDoc} */ @Override public boolean handleMessage(final Object message) { if (message instanceof Command) { //TODO(barp): Map with a POJO mapper [MAI-184] final Command command = (Command) message; final ObjectNode commandNode = (ObjectNode) command.getCommand(); final String commandString = commandNode.get("command").asText(); switch (commandString) { case COMMAND_GET_METRICS: _metrics.incrementCounter(GET_METRICS_COUNTER); _connection.getTelemetry().tell(new MetricsListRequest(), _connection.getSelf()); break; case COMMAND_SUBSCRIBE_METRIC: { _metrics.incrementCounter(SUBSCRIBE_COUNTER); final String service = commandNode.get("service").asText(); final String metric = commandNode.get("metric").asText(); final String statistic = commandNode.get("statistic").asText(); subscribe(service, metric, statistic); break; } case COMMAND_UNSUBSCRIBE_METRIC: { _metrics.incrementCounter(UNSUBSCRIBE_COUNTER); final String service = commandNode.get("service").asText(); final String metric = commandNode.get("metric").asText(); final String statistic = commandNode.get("statistic").asText(); unsubscribe(service, metric, statistic); break; } default: return false; } } else if (message instanceof NewMetric) { //TODO(barp): Map with a POJO mapper [MAI-184] _metrics.incrementCounter(NEW_METRIC_COUNTER); final NewMetric newMetric = (NewMetric) message; processNewMetric(newMetric); } else if (message instanceof MetricReport) { _metrics.incrementCounter(METRIC_REPORT_COUNTER); final MetricReport report = (MetricReport) message; processMetricReport(report); } else if (message instanceof MetricsList) { _metrics.incrementCounter(METRIC_LIST_COUNTER); final MetricsList metricsList = (MetricsList) message; processMetricsList(metricsList); } else { return false; } return true; } /** * {@inheritDoc} */ @Override public void initializeMetrics(final Metrics metrics) { _metrics = metrics; metrics.resetCounter(METRIC_LIST_COUNTER); metrics.resetCounter(METRIC_REPORT_COUNTER); metrics.resetCounter(NEW_METRIC_COUNTER); metrics.resetCounter(UNSUBSCRIBE_COUNTER); metrics.resetCounter(SUBSCRIBE_COUNTER); metrics.resetCounter(GET_METRICS_COUNTER); } /** * Generate a Steno log compatible representation. * * @return Steno log compatible representation. */ @LogValue public Object toLogValue() { // NOTE: Do not log connection context as this creates a circular reference return LogValueMapFactory.builder(this).put("subscriptions", _subscriptions).build(); } /** * {@inheritDoc} */ @Override public String toString() { return toLogValue().toString(); } private void processNewMetric(final NewMetric newMetric) { final ObjectNode n = new ObjectNode(OBJECT_MAPPER.getNodeFactory()); n.put("service", newMetric.getService()); n.put("metric", newMetric.getMetric()); n.put("statistic", newMetric.getStatistic()); _connection.sendCommand(COMMAND_NEW_METRIC, n); } private void processMetricReport(final MetricReport report) { final Map<String, Set<String>> metrics = _subscriptions.get(report.getService()); if (metrics == null) { LOGGER.trace().setMessage("Not sending MetricReport") .addData("reason", "service not found in subscriptions").addData("service", report.getService()) .log(); return; } final Set<String> stats = metrics.get(report.getMetric()); if (stats == null) { LOGGER.trace().setMessage("Not sending MetricReport") .addData("reason", "metric not found in subscriptions").addData("metric", report.getMetric()) .log(); return; } if (!stats.contains(report.getStatistic())) { LOGGER.trace().setMessage("Not sending MetricReport") .addData("reason", "statistic not found in subscriptions") .addData("statistic", report.getStatistic()).log(); return; } //TODO(barp): Map with a POJO mapper [MAI-184] final ObjectNode event = new ObjectNode(OBJECT_MAPPER.getNodeFactory()); event.put("server", report.getHost()); event.put("service", report.getService()); event.put("metric", report.getMetric()); event.put("timestamp", report.getPeriodStart().getMillis()); event.put("statistic", report.getStatistic()); event.put("data", report.getValue()); _connection.sendCommand(COMMAND_REPORT_METRIC, event); } private void processMetricsList(final MetricsList metricsList) { //TODO(barp): Map with a POJO mapper [MAI-184] final ObjectNode dataNode = JsonNodeFactory.instance.objectNode(); final ArrayNode services = JsonNodeFactory.instance.arrayNode(); for (final Map.Entry<String, Map<String, Set<String>>> service : metricsList.getMetrics().entrySet()) { final ObjectNode serviceObject = JsonNodeFactory.instance.objectNode(); serviceObject.put("name", service.getKey()); final ArrayNode metrics = JsonNodeFactory.instance.arrayNode(); for (final Map.Entry<String, Set<String>> metric : service.getValue().entrySet()) { final ObjectNode metricObject = JsonNodeFactory.instance.objectNode(); metricObject.put("name", metric.getKey()); final ArrayNode stats = JsonNodeFactory.instance.arrayNode(); for (final String statistic : metric.getValue()) { final ObjectNode statsObject = JsonNodeFactory.instance.objectNode(); statsObject.put("name", statistic); statsObject.set("children", JsonNodeFactory.instance.arrayNode()); stats.add(statsObject); } metricObject.set("children", stats); metrics.add(metricObject); } serviceObject.set("children", metrics); services.add(serviceObject); } dataNode.set("metrics", services); _connection.sendCommand(COMMAND_METRICS_LIST, dataNode); } private void subscribe(final String service, final String metric, final String statistic) { if (!_subscriptions.containsKey(service)) { _subscriptions.put(service, Maps.<String, Set<String>>newHashMap()); } final Map<String, Set<String>> metrics = _subscriptions.get(service); if (!metrics.containsKey(metric)) { metrics.put(metric, Sets.<String>newHashSet()); } final Set<String> statistics = metrics.get(metric); if (!statistics.contains(statistic)) { statistics.add(statistic); } } private void unsubscribe(final String service, final String metric, final String statistic) { if (!_subscriptions.containsKey(service)) { return; } final Map<String, Set<String>> metrics = _subscriptions.get(service); if (!metrics.containsKey(metric)) { return; } final Set<String> statistics = metrics.get(metric); if (statistics.contains(statistic)) { statistics.remove(statistic); } } private final Map<String, Map<String, Set<String>>> _subscriptions = Maps.newHashMap(); private final Connection _connection; private Metrics _metrics; private static final String COMMAND_METRICS_LIST = "metricsList"; private static final String COMMAND_REPORT_METRIC = "report"; private static final String COMMAND_NEW_METRIC = "newMetric"; private static final String COMMAND_SUBSCRIBE_METRIC = "subscribe"; private static final String COMMAND_UNSUBSCRIBE_METRIC = "unsubscribe"; private static final String COMMAND_GET_METRICS = "getMetrics"; private static final String METRICS_PREFIX = "message_processor/metric/"; private static final String METRIC_LIST_COUNTER = METRICS_PREFIX + "metrics_list"; private static final String METRIC_REPORT_COUNTER = METRICS_PREFIX + "metric_report"; private static final String NEW_METRIC_COUNTER = METRICS_PREFIX + "new_metric"; private static final String UNSUBSCRIBE_COUNTER = METRICS_PREFIX + "command/unsubscribe"; private static final String SUBSCRIBE_COUNTER = METRICS_PREFIX + "command/aubscribe"; private static final String GET_METRICS_COUNTER = METRICS_PREFIX + "command/get_metrics"; private static final ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getInstance(); private static final Logger LOGGER = LoggerFactory.getLogger(MetricMessagesProcessor.class); }