package com.linkedin.databus.client.request;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.
*
*/
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import org.apache.log4j.Logger;
import org.jboss.netty.handler.codec.http.HttpMethod;
import com.linkedin.databus.client.DatabusHttpClientImpl;
import com.linkedin.databus.client.pub.DatabusRegistration;
import com.linkedin.databus.client.pub.DatabusV3Registration;
import com.linkedin.databus.client.pub.DbusPartitionInfo;
import com.linkedin.databus.client.pub.RegistrationId;
import com.linkedin.databus.client.pub.mbean.UnifiedClientStats;
import com.linkedin.databus.client.registration.DatabusMultiPartitionRegistration;
import com.linkedin.databus.core.monitoring.mbean.DbusEventsStatisticsCollector;
import com.linkedin.databus.core.monitoring.mbean.DbusEventsTotalStats;
import com.linkedin.databus.core.monitoring.mbean.StatsCollectors;
import com.linkedin.databus2.core.container.monitoring.mbean.DbusHttpTotalStats;
import com.linkedin.databus2.core.container.request.AbstractStatsRequestProcessor;
import com.linkedin.databus2.core.container.request.DatabusRequest;
import com.linkedin.databus2.core.container.request.InvalidRequestParamValueException;
import com.linkedin.databus2.core.container.request.RequestProcessingException;
public class ClientStatsRequestProcessor extends AbstractStatsRequestProcessor
{
public static final String MODULE = ClientStatsRequestProcessor.class.getName();
public static final Logger LOG = Logger.getLogger(MODULE);
public static final String COMMAND_NAME = "clientStats";
private final static String OUTBOUND_EVENTS_TOTAL_KEY = "outbound/events/total";
private final static String OUTBOUND_EVENTS_SOURCES_KEY = "outbound/events/sources";
private final static String OUTBOUND_EVENTS_SOURCE_PREFIX = "outbound/events/source/";
private final static String OUTBOUND_EVENTS_CLIENTS_KEY = "outbound/events/clients";
private final static String OUTBOUND_EVENTS_CLIENT_PREFIX = "outbound/events/client/";
private final static String INBOUND_HTTP_TOTAL_KEY = "inbound/http/total";
private final static String INBOUND_HTTP_SOURCES_KEY = "inbound/http/sources";
private final static String INBOUND_HTTP_SOURCE_PREFIX = "inbound/http/source/";
private final static String INBOUND_HTTP_RELAYS_KEY = "inbound/http/relays";
private final static String INBOUND_HTTP_RELAYS_PREFIX = "inbound/http/relay/";
private final static String INBOUND_EVENTS_TOTAL_KEY = "inbound/events/total";
private final static String BOOTSTRAP_EVENTS_TOTAL_KEY = "bootstrap/events/total";
private final static String INBOUND_EVENTS_SOURCES_KEY = "inbound/events/sources"; // was "outbound/events/sources"
/** Stream/relay events (inbound into client lib's event buffer) for the registration. */
private final static String INBOUND_EVENTS_REG_KEY_PREFIX = "inbound/events/registration/";
/** Bootstrap events (inbound into client lib's event buffer) for the registration. */
private final static String BOOTSTRAP_EVENTS_REG_KEY_PREFIX = "bootstrap/events/registration/";
/** Stream/relay-mode callbacks for the registration (outbound to consumer/app). */
// This key is very poorly named...
private final static String INBOUND_CALLBACKS_REG_KEY_PREFIX = "inbound/callbacks/registration/";
/** Bootstrap-mode callbacks for the registration (outbound to consumer/app). */
private final static String BOOTSTRAP_CALLBACKS_REG_KEY_PREFIX = "bootstrap/callbacks/registration/";
/**
* Unified relay/bootstrap statistics for the registration (both inbound to the client
* lib's event buffer and outbound to the consumer/app, but mostly the latter).
*/
// "Registration" might be misnomer? Client impl's loop is over relay groups, each
// corresponding to a set of subscriptions (sources) and each with its own UnifiedClientStats
// object.
private final static String UNIFIED_REG_KEY_PREFIX = "unified/registration/";
/**
* Unified relay/bootstrap statistics aggregated across registrations (both inbound to
* the client lib's event buffer and outbound to the consumer/app, but mostly the latter).
*/
// Note that there is currently no corresponding REST interface to the aggregated
// ConsumerCallbackStats objects.
private final static String UNIFIED_TOTAL_KEY = "unified/total";
private final DatabusHttpClientImpl _client;
public ClientStatsRequestProcessor(ExecutorService executorService, DatabusHttpClientImpl client)
{
super(COMMAND_NAME, executorService);
_client = client;
}
@Override
public boolean doProcess(String category, DatabusRequest request)
throws IOException, RequestProcessingException
{
boolean success = true;
if (category.equals(OUTBOUND_EVENTS_TOTAL_KEY))
{
processEventsTotalStats(_client.getOutboundEventStatisticsCollector(), request);
}
else if (category.equals(OUTBOUND_EVENTS_SOURCES_KEY))
{
processEventsSourcesList(_client.getOutboundEventStatisticsCollector(), request);
}
else if (category.startsWith(OUTBOUND_EVENTS_SOURCE_PREFIX))
{
processEventsSourceStats(_client.getOutboundEventStatisticsCollector(),
OUTBOUND_EVENTS_SOURCE_PREFIX,
request);
}
else if (category.equals(OUTBOUND_EVENTS_CLIENTS_KEY))
{
processEventsPeersList(_client.getOutboundEventStatisticsCollector(), request);
}
else if (category.startsWith(OUTBOUND_EVENTS_CLIENT_PREFIX))
{
processEventsPeerStats(_client.getOutboundEventStatisticsCollector(),
OUTBOUND_EVENTS_CLIENT_PREFIX,
request);
}
else if (category.equals(INBOUND_HTTP_TOTAL_KEY))
{
processOutboundHttpTotalStats(request);
}
else if (category.equals(INBOUND_HTTP_SOURCES_KEY))
{
processOutboundHttpSourcesList(request);
}
else if (category.startsWith(INBOUND_HTTP_SOURCE_PREFIX))
{
processOutboundHttpSourceStats(request);
}
else if (category.equals(INBOUND_HTTP_RELAYS_KEY))
{
processOutboundHttpClientsList(request);
}
else if (category.startsWith(INBOUND_HTTP_RELAYS_PREFIX))
{
processOutboundHttpClientStats(request);
}
else if (category.equals(INBOUND_EVENTS_TOTAL_KEY))
{
processEventsTotalStats(_client.getInboundEventStatisticsCollector(), request);
}
else if (category.equals(BOOTSTRAP_EVENTS_TOTAL_KEY))
{
processEventsTotalStats(_client.getBootstrapEventsStatsCollector(), request);
}
else if (category.equals(INBOUND_EVENTS_SOURCES_KEY))
{
processEventsSourcesList(_client.getInboundEventStatisticsCollector(), request);
}
else if (category.startsWith(INBOUND_EVENTS_REG_KEY_PREFIX))
{
/**
* TODO : The reason why there is diverging code below is because V2 and V3 is using different registration objects
* which results in different containers for V2 and V3 registrations.
* Once we make V3 use DatabusRegistration, we should be having one call.
*
* Note: Couldnt use instanceof for differentiating V2/V3 as V3 Client is not visible here.
*/
if ( _client.getClass().getSimpleName().equalsIgnoreCase("DatabusHttpClientImpl") )
processInboundEventsRegistration(request);
else
processInboundEventsRegistrationV3(request);
}
else if (category.startsWith(BOOTSTRAP_EVENTS_REG_KEY_PREFIX))
{
/**
* TODO : The reason why there is diverging code below is because V2 and V3 is using different registration objects
* which results in different containers for V2 and V3 registrations.
* Once we make V3 use DatabusRegistration, we should be having one call.
*
* Note: Couldnt use instanceof for differentiating V2/V3 as V3 Client is not visible here.
*/
if ( _client.getClass().getSimpleName().equalsIgnoreCase("DatabusHttpClientImpl") )
processBootstrapEventsRegistration(request);
else
processBootstrapEventsRegistrationV3(request);
}
else if (category.startsWith(INBOUND_CALLBACKS_REG_KEY_PREFIX))
{
/**
* TODO : The reason why there is diverging code below is because V2 and V3 is using different registration objects
* which results in different containers for V2 and V3 registrations.
* Once we make V3 use DatabusRegistration, we should be having one call.
*
* Note: Couldnt use instanceof for differentiating V2/V3 as V3 Client is not visible here.
*/
if ( _client.getClass().getSimpleName().equalsIgnoreCase("DatabusHttpClientImpl") )
processInboundCallbacksRegistration(request);
else
processInboundCallbacksRegistrationV3(request);
}
else if (category.startsWith(BOOTSTRAP_CALLBACKS_REG_KEY_PREFIX))
{
/**
* TODO : The reason why there is diverging code below is because V2 and V3 is using different registration objects
* which results in different containers for V2 and V3 registrations.
* Once we make V3 use DatabusRegistration, we should be having one call.
*
* Note: Couldnt use instanceof for differentiating V2/V3 as V3 Client is not visible here.
*/
if ( _client.getClass().getSimpleName().equalsIgnoreCase("DatabusHttpClientImpl") )
processBootstrapCallbacksRegistration(request);
else
processBootstrapCallbacksRegistrationV3(request);
}
else if (category.startsWith(UNIFIED_REG_KEY_PREFIX))
{
/**
* TODO : The reason why there is diverging code below is because V2 and V3 is using different registration objects
* which results in different containers for V2 and V3 registrations.
* Once we make V3 use DatabusRegistration, we should be having one call.
*
* Note: Couldn't use instanceof for differentiating V2/V3 as V3 Client is not visible here.
*/
if ( _client.getClass().getSimpleName().equalsIgnoreCase("DatabusHttpClientImpl") )
processUnifiedRegistration(request);
else
processUnifiedRegistrationV3(request);
}
else if (category.equals(UNIFIED_TOTAL_KEY))
{
processUnifiedTotalStats(_client.getUnifiedClientStatsCollectors(), request);
}
else
{
success = false;
}
return success;
}
private void processUnifiedTotalStats(StatsCollectors<UnifiedClientStats> statsCollectors,
DatabusRequest request) throws IOException
{
if (null == statsCollectors) return;
UnifiedClientStats unifiedTotalStats = statsCollectors.getStatsCollector();
if (null == unifiedTotalStats) return;
writeJsonObjectToResponse(unifiedTotalStats, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(unifiedTotalStats, request);
}
}
private void processEventsTotalStats(DbusEventsStatisticsCollector statsCollector,
DatabusRequest request) throws IOException
{
if (null == statsCollector) return;
DbusEventsTotalStats totalStatsMBean = statsCollector.getTotalStats();
if (null == totalStatsMBean) return;
writeJsonObjectToResponse(totalStatsMBean, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(totalStatsMBean, request);
}
}
private void processEventsSourcesList(DbusEventsStatisticsCollector statsCollector,
DatabusRequest request) throws IOException
{
if (null == statsCollector) return;
List<Integer> sourcesList = statsCollector.getSources();
writeJsonObjectToResponse(sourcesList, request);
}
private void processEventsSourceStats(DbusEventsStatisticsCollector statsCollector,
String prefix,
DatabusRequest request)
throws IOException, RequestProcessingException
{
if (null == statsCollector) return;
String category = request.getParams().getProperty(DatabusRequest.PATH_PARAM_NAME);
String sourceIdStr = category.substring(prefix.length());
int sourceId = -1;
try
{
sourceId = Integer.valueOf(sourceIdStr);
}
catch (NumberFormatException nfe)
{
throw new InvalidRequestParamValueException(request.getName(), prefix, sourceIdStr);
}
DbusEventsTotalStats sourceStats = statsCollector.getSourceStats(sourceId);
if (null == sourceStats)
{
throw new InvalidRequestParamValueException(request.getName(), prefix, sourceIdStr);
}
writeJsonObjectToResponse(sourceStats, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(sourceStats, request);
}
}
private void processEventsPeersList(DbusEventsStatisticsCollector statsCollector,
DatabusRequest request) throws IOException
{
if (null == statsCollector) return;
List<String> clientsList = statsCollector.getPeers();
writeJsonObjectToResponse(clientsList, request);
}
private void processEventsPeerStats(DbusEventsStatisticsCollector statsCollector,
String prefix,
DatabusRequest request)
throws IOException, RequestProcessingException
{
if (null == statsCollector) return;
String category = request.getParams().getProperty(DatabusRequest.PATH_PARAM_NAME);
String client = category.substring(prefix.length());
DbusEventsTotalStats clientStats = statsCollector.getPeerStats(client);
if (null == clientStats)
{
throw new InvalidRequestParamValueException(request.getName(), prefix, client);
}
writeJsonObjectToResponse(clientStats, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(clientStats, request);
}
}
private void processOutboundHttpTotalStats(DatabusRequest request) throws IOException
{
DbusHttpTotalStats totalStats = _client.getHttpStatsCollector().getTotalStats();
if (null == totalStats) return;
writeJsonObjectToResponse(totalStats, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(totalStats, request);
}
}
private void processOutboundHttpSourcesList(DatabusRequest request) throws IOException
{
List<Integer> sourcesList = _client.getHttpStatsCollector().getSources();
writeJsonObjectToResponse(sourcesList, request);
}
private void processOutboundHttpClientsList(DatabusRequest request) throws IOException
{
List<String> clientsList = _client.getHttpStatsCollector().getPeers();
writeJsonObjectToResponse(clientsList, request);
}
private void processOutboundHttpSourceStats(DatabusRequest request)
throws IOException, RequestProcessingException
{
String category = request.getParams().getProperty(DatabusRequest.PATH_PARAM_NAME);
String sourceIdStr = category.substring(INBOUND_HTTP_SOURCE_PREFIX.length());
int sourceId = -1;
try
{
sourceId = Integer.valueOf(sourceIdStr);
}
catch (NumberFormatException nfe)
{
throw new InvalidRequestParamValueException(request.getName(), INBOUND_HTTP_SOURCE_PREFIX, sourceIdStr);
}
DbusHttpTotalStats sourceStats = _client.getHttpStatsCollector().getSourceStats(sourceId);
if (null == sourceStats)
{
throw new InvalidRequestParamValueException(request.getName(), INBOUND_HTTP_SOURCE_PREFIX, sourceIdStr);
}
writeJsonObjectToResponse(sourceStats, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(sourceStats, request);
}
}
private void processOutboundHttpClientStats(DatabusRequest request)
throws IOException, RequestProcessingException
{
String category = request.getParams().getProperty(DatabusRequest.PATH_PARAM_NAME);
String client = category.substring(INBOUND_HTTP_RELAYS_PREFIX.length());
DbusHttpTotalStats clientStats = _client.getHttpStatsCollector().getPeerStats(client);
if (null == clientStats)
{
throw new InvalidRequestParamValueException(request.getName(), INBOUND_HTTP_RELAYS_PREFIX, client);
}
writeJsonObjectToResponse(clientStats, request);
if (request.getRequestType() == HttpMethod.PUT || request.getRequestType() == HttpMethod.POST)
{
enableOrResetStatsMBean(clientStats, request);
}
}
private void processInboundEventsRegistration(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusRegistration reg = findRegistration(request, INBOUND_EVENTS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getRelayEventStats().getTotalStats(), request);
}
private void processBootstrapEventsRegistration(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusRegistration reg = findRegistration(request, BOOTSTRAP_EVENTS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getBootstrapEventStats().getTotalStats(), request);
}
private void processInboundCallbacksRegistration(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusRegistration reg = findRegistration(request, INBOUND_CALLBACKS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getRelayCallbackStats(), request);
}
private void processBootstrapCallbacksRegistration(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusRegistration reg = findRegistration(request, BOOTSTRAP_CALLBACKS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getBootstrapCallbackStats(), request);
}
private void processUnifiedRegistration(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusRegistration reg = findRegistration(request, UNIFIED_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getUnifiedClientStats(), request);
}
private DatabusRegistration findRegistration(DatabusRequest request, String prefix) throws RequestProcessingException
{
String category = request.getParams().getProperty(DatabusRequest.PATH_PARAM_NAME);
String registrationIdStr = category.substring(prefix.length());
RegistrationId regId = new RegistrationId(registrationIdStr);
Collection<DatabusRegistration> regs = _client.getAllRegistrations();
for (DatabusRegistration r : regs)
{
if (regId.equals(r.getRegistrationId()))
return r;
if (r instanceof DatabusMultiPartitionRegistration)
{
Map<DbusPartitionInfo, DatabusRegistration> childRegs = ((DatabusMultiPartitionRegistration)r).getPartitionRegs();
for (Entry<DbusPartitionInfo, DatabusRegistration> e : childRegs.entrySet())
if ( regId.equals(e.getValue().getRegistrationId()))
return e.getValue();
}
}
throw new RequestProcessingException("Unable to find registration (" + regId + ") ");
}
private void processInboundEventsRegistrationV3(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusV3Registration reg = findV3Registration(request, INBOUND_EVENTS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getRelayEventStats().getTotalStats(), request);
}
private void processBootstrapEventsRegistrationV3(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusV3Registration reg = findV3Registration(request, BOOTSTRAP_EVENTS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getBootstrapEventStats().getTotalStats(), request);
}
private void processInboundCallbacksRegistrationV3(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusV3Registration reg = findV3Registration(request, INBOUND_CALLBACKS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getRelayCallbackStats(), request);
}
private void processBootstrapCallbacksRegistrationV3(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusV3Registration reg = findV3Registration(request, BOOTSTRAP_CALLBACKS_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getBootstrapCallbackStats(), request);
}
private void processUnifiedRegistrationV3(DatabusRequest request)
throws IOException, RequestProcessingException
{
DatabusV3Registration reg = findV3Registration(request, UNIFIED_REG_KEY_PREFIX);
writeJsonObjectToResponse(reg.getUnifiedClientStats(), request);
}
private DatabusV3Registration findV3Registration(DatabusRequest request, String prefix)
throws InvalidRequestParamValueException
{
String category = request.getParams().getProperty(DatabusRequest.PATH_PARAM_NAME);
String registrationIdStr = category.substring(prefix.length());
DatabusV3Registration reg = _client.getRegistration(new RegistrationId(registrationIdStr));
if ( null == reg )
{
LOG.warn("Invalid registrationId: " + registrationIdStr );
throw new InvalidRequestParamValueException(request.getName(), prefix, "No data available for this RegistrationId yet" );
}
return reg;
}
}