/******************************************************************************* * Copyright (c) 2006-2011 eBay Inc. 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 *******************************************************************************/ package org.ebayopensource.turmeric.runtime.error.cassandra.handler; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.List; import java.util.Random; import me.prettyprint.hector.api.exceptions.HectorException; import org.ebayopensource.turmeric.common.v1.types.CommonErrorData; import org.ebayopensource.turmeric.common.v1.types.ErrorData; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceExceptionInterface; import org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler; import org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandlerStage; import org.ebayopensource.turmeric.runtime.common.pipeline.MessageContext; import org.ebayopensource.turmeric.runtime.common.types.SOAConstants; import org.ebayopensource.turmeric.runtime.common.types.SOAHeaders; import org.ebayopensource.turmeric.runtime.error.cassandra.dao.ErrorByIdDAO; import org.ebayopensource.turmeric.runtime.error.cassandra.dao.ErrorCountsDAO; import org.ebayopensource.turmeric.runtime.error.cassandra.dao.ErrorValueDAO; import org.ebayopensource.turmeric.runtime.error.cassandra.model.ErrorById; import org.ebayopensource.turmeric.runtime.error.cassandra.model.ErrorValue; import org.ebayopensource.turmeric.utils.cassandra.service.CassandraManager; // TODO: Auto-generated Javadoc /** * The Class CassandraErrorLoggingHandler. * * @author manuelchinea */ public class CassandraErrorLoggingHandler implements LoggingHandler { /** The Constant KEY_SEPARATOR. */ public static final String KEY_SEPARATOR = "|"; /** The cluster name. */ private String clusterName; /** The embedded. */ private Boolean embedded; /** The error counts dao. */ private ErrorCountsDAO errorCountsDao = null; /** The error dao. */ private ErrorByIdDAO errorDao = null; /** The error value dao. */ private ErrorValueDAO errorValueDao = null; /** The host address. */ private String hostAddress; /** The keyspace name. */ private String keyspaceName; /** The random generator. */ private Random randomGenerator; /** * Instantiates a new cassandra error logging handler. */ public CassandraErrorLoggingHandler() { } /** * Creates the cf. * * @return the cluster name */ /** * Gets the cluster name. * * @return the cluster name */ public String getClusterName() { return clusterName; } /** * Gets the comparator. * * @return the comparator */ /** * Gets the embedded. * * @return the embedded */ public Boolean getEmbedded() { return embedded; } /** * Gets the host address. * * @return the host address */ public String getHostAddress() { return hostAddress; } /** * Gets the inet address. * * @return the inet address * @throws ServiceException * the service exception */ private String getInetAddress() throws ServiceException { try { return InetAddress.getLocalHost().getCanonicalHostName(); } catch (UnknownHostException x) { throw new ServiceException("Unkonwn host name", x); } } /** * Gets the keyspace name. * * @return the keyspace name */ public String getKeyspaceName() { return keyspaceName; } /** * Inits the CassandraErrorLoggingHandler. * * @param ctx * the ctx * @throws ServiceException * the service exception * @see org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler#init(org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler.InitContext) */ @Override public void init(InitContext ctx) throws ServiceException { java.util.Map<String, String> options = ctx.getOptions(); clusterName = options.get("cluster-name"); hostAddress = options.get("host-address"); keyspaceName = options.get("keyspace-name"); if (this.isInvalidString(clusterName) || this.isInvalidString(hostAddress) || this.isInvalidString(keyspaceName)) { throw new ServiceException("Invalid cassandra options. Empty clusterName/hostAddress/keyspaceName"); } String randomGeneratorClassName = options.get("random-generator-class-name"); if ((randomGeneratorClassName == null) || randomGeneratorClassName.isEmpty()) { randomGenerator = new Random(System.currentTimeMillis()); } else { try { Class<?> theClass = Class.forName(randomGeneratorClassName); randomGenerator = (Random) theClass.newInstance(); } catch (Exception e) { throw new ServiceException("Error instancing random generator class", e); } } String embeddedOption = options.get("embedded"); this.embedded = Boolean.valueOf(embeddedOption); if (this.embedded) { CassandraManager.initialize(); } errorDao = new ErrorByIdDAO(clusterName, hostAddress, keyspaceName, Long.class, org.ebayopensource.turmeric.runtime.error.cassandra.model.ErrorById.class, "ErrorsById"); errorValueDao = new ErrorValueDAO(clusterName, hostAddress, keyspaceName, String.class, org.ebayopensource.turmeric.runtime.error.cassandra.model.ErrorValue.class, "ErrorValues"); errorCountsDao = new ErrorCountsDAO(clusterName, hostAddress, keyspaceName); } /** * Checks if is invalid string. * * @param str * the str * @return true, if is invalid string */ private boolean isInvalidString(String str) { return (str == null) || str.isEmpty(); } /** * Log error. * * @param ctx * the ctx * @param e * the e * @throws ServiceException * the service exception * @see org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler#logError(org.ebayopensource.turmeric.runtime.common.pipeline.MessageContext, * java.lang.Throwable) */ @Override public void logError(MessageContext ctx, Throwable e) throws ServiceException { if (e instanceof ServiceExceptionInterface) { ServiceExceptionInterface serviceException = (ServiceExceptionInterface) e; List<CommonErrorData> errorsToStore = serviceException.getErrorMessage().getError(); String consumerName = this.retrieveConsumerName(ctx); String serviceAdminName = ctx.getAdminName(); String operationName = ctx.getOperationName(); boolean serverSide = !ctx.getServiceId().isClientSide(); String serverName = this.getInetAddress(); long now = System.currentTimeMillis(); this.persistErrors(errorsToStore, serverName, serviceAdminName, operationName, serverSide, consumerName, now); } else { // TODO: create an error data from the exception itself ? } } /** * Log processing stage. * * @param ctx * the ctx * @param stage * the stage * @throws ServiceException * the service exception * @see org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler#logProcessingStage(org.ebayopensource.turmeric.runtime.common.pipeline.MessageContext, * org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandlerStage) */ @Override public void logProcessingStage(MessageContext ctx, LoggingHandlerStage stage) throws ServiceException { } /** * Log response resident error. * * @param ctx * the ctx * @param errorData * the error data * @throws ServiceException * the service exception * @see org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler#logResponseResidentError(org.ebayopensource.turmeric.runtime.common.pipeline.MessageContext, * org.ebayopensource.turmeric.common.v1.types.ErrorData) */ @Override public void logResponseResidentError(MessageContext ctx, ErrorData errorData) throws ServiceException { List<? extends ErrorData> errorsToStore = Collections.singletonList(errorData); String consumerName = this.retrieveConsumerName(ctx); String serviceAdminName = ctx.getAdminName(); String operationName = ctx.getOperationName(); boolean serverSide = !ctx.getServiceId().isClientSide(); String serverName = this.getInetAddress(); long now = System.currentTimeMillis(); this.persistErrors(errorsToStore, serverName, serviceAdminName, operationName, serverSide, consumerName, now); } /** * Log warning. * * @param ctx * the ctx * @param e * the e * @throws ServiceException * the service exception * @see org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandler#logWarning(org.ebayopensource.turmeric.runtime.common.pipeline.MessageContext, * java.lang.Throwable) */ @Override public void logWarning(MessageContext ctx, Throwable e) throws ServiceException { this.logError(ctx, e); } /** * Persist errors. * * @param errorsList * the errors to store * @param serverName * the server name * @param srvcAdminName * the srvc admin name * @param opName * the op name * @param serverSide * the server side * @param consumerName * the consumer name * @param timeStamp * the time stamp * @throws ServiceException * the service exception */ public void persistErrors(List<? extends ErrorData> errorsList, String serverName, String srvcAdminName, String opName, boolean serverSide, String consumerName, long timeStamp) throws ServiceException { try { for (ErrorData errorData : errorsList) { CommonErrorData commonErrorData = (CommonErrorData) errorData; String errorMessage = commonErrorData.getMessage(); org.ebayopensource.turmeric.runtime.error.cassandra.model.ErrorById errorToSave = new ErrorById( commonErrorData); ErrorValue errorValue = new ErrorValue(errorToSave, serverName, errorMessage, srvcAdminName, opName, consumerName, timeStamp, serverSide, 0, randomGenerator.nextInt()); String errorValueKey = errorValue.getKey(); errorDao.save(errorToSave.getErrorId(), errorToSave, timeStamp, errorValueKey); // not good. What if 2 different error values for the same error occurs at the same time? errorValueDao.save(errorValueKey, errorValue); errorCountsDao.saveErrorCounts(errorToSave, errorValue, errorValueKey, timeStamp, 1); } } catch (HectorException he) { throw new ServiceException("Exception logging error in Cassandra cluster", he); } } /** * Retrieve consumer name. * * @param ctx * the ctx * @return the string * @throws ServiceException * the service exception */ private String retrieveConsumerName(MessageContext ctx) throws ServiceException { String result = ctx.getRequestMessage().getTransportHeader(SOAHeaders.USECASE_NAME); if (result == null) result = SOAConstants.DEFAULT_USE_CASE; return result; } }