All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.service.Event Maven / Gradle / Ivy

/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
/*
 * Created on Feb 3, 2009
 */

package com.bigdata.service;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import com.bigdata.counters.AbstractStatisticsCollector;
import com.bigdata.journal.ITransactionService;

/**
 * An event. Events are queued by the {@link IBigdataClient} and self-reported
 * periodically to the {@link ILoadBalancerService}. The event is assigned a
 * {@link UUID} when it is created and the {@link ILoadBalancerService} assigned
 * start and end event times based on its local clock as the events are received
 * (this helps to reduce the demand on the {@link ITransactionService} for
 * global timestamp).
 * 
 * @author Bryan Thompson
 * @version $Id$
 * 
 * @todo compact event serialization when reporting to the
 *       {@link ILoadBalancerService}, including factoring out of the common
 *       metadata (some stuff will always be the same for a given reported and
 *       does not need to be reported with each event).
 * 
 * @todo Add a level property to events? (but events are not meant to replace
 *       logging)
 */
public class Event implements Serializable {

    protected static transient final Logger log = Logger.getLogger(Event.class);

    /**
     * 
     */
    private static final long serialVersionUID = 2651293369056916231L;

    protected transient IBigdataFederation fed;

    /**
     * Unique event identifier.
     */
    public final UUID eventUUID;

    /**
     * The host on which the event was generated.
     */
    public final String hostname;
    
    /**
     * The most interesting class or interface for the service which generated
     * that event.
     */
    public final Class serviceIface;
    
    /**
     * The name of the service which generated the event.
     */
    public final String serviceName;
    
    /**
     * The {@link UUID} for the service which generated that event.
     */
    public final UUID serviceUUID;
    
    /**
     * The resource for which the event is reported (store file, index name,
     * etc). Note that the {@link #serviceUUID} will also be reported.
     */
    public final EventResource resource;
    
    /**
     * Major event type (classification or category).
     */
    public final Object majorEventType;

    /**
     * Minor event type (classification or category).
     */
    public final Object minorEventType;

    /**
     * Event details.
     */
    protected Map details;
    
    /**
     * Event details.
     */
    public Map getDetails() {
        
        return details;
        
    }

    /**
     * Add details.
     * @param details
     * @return This {@link Event}.
     */
    public Event addDetails(final Map details) {

        if(details == null)
            throw new IllegalArgumentException();
        
        synchronized (this) {

            if (this.details == null) {

                this.details = newDetails();

            }

            this.details.putAll(details);

        }

        return this;

    }
    
    /**
     * Factory for the details hash map. This is a synchronized collection since
     * the event can be serialized concurrent with {@link #end()} which would
     * otherwise lead to a {@link ConcurrentModificationException}. It can not
     * be a {@link ConcurrentHashMap} since that class does not support
     * null values.
     */
    protected static Map newDetails() {
        
        return Collections.synchronizedMap(new HashMap());
        
    }
    
    /**
     * Add a detail.
     * @param name
     * @param value
     * @return This {@link Event}.
     */
    public Event addDetail(final String name, final Object value) {

        if(name == null)
            throw new IllegalArgumentException();

        synchronized (this) {

            if (details == null) {

                details = newDetails();

            }

            details.put(name, value);

        }
        
        return this;
        
    }

    /**
     * The event start time. Assigned locally. The recipient may use [endTime -
     * startTime] to adjust the event to its local clock.
     */
    protected long startTime;

    /**
     * The event end time. Assigned locally. The recipient may use [endTime -
     * startTime] to adjust the event to its local clock.
     */
    protected long endTime;

    /**
     * The time when the event was received. The recipient may use [endTime -
     * startTime] to adjust the event to its local clock.
     */
    transient protected long receiptTime;
    
    /**
     * true iff the event event has been generated.
     */
    protected boolean complete = false;

    /**
     * true iff the event event has been generated.
     */
    public boolean isComplete() {
        
        return complete;
        
    }
    
    public long getStartTime() {

        return startTime;

    }

    public long getEndTime() {

        return endTime;

    }

    /**
     * The elapsed time for the event.
     */
    public long getElapsed() {
        
        if (endTime == 0)
            return System.currentTimeMillis() - startTime;
        else
            return endTime - startTime;
        
    }
    
    public Event(final IBigdataFederation fed, final EventResource resource,
            final Object majorEventType) {
        
        this(fed, resource, majorEventType, (Map) null/* details */);
        
    }

    /**
     * Event ctor.
     * 
     * @param fed
     *            The federation object (used to send the event to an
     *            aggregator).
     * @param resource
     *            The resource for which the event was generated (store, index
     *            partition, etc).
     * @param majorEventType
     *            The major type of the event (use of enums is encouraged).
     * @param details
     *            Optional details for the event.
     */
    public Event(final IBigdataFederation fed, final EventResource resource,
            final Object majorEventType, final Map details) {

        this(fed, resource, majorEventType, ""/* minorEventType */, details);
        
    }
    
    /**
     * Sub-event ctor.
     * 
     * @param fed
     *            The federation object (used to send the event to an
     *            aggregator).
     * @param resource
     *            The resource for which the event was generated (store, index
     *            partition, etc).
     * @param majorEventType
     *            The major type of the event (use of enums is encouraged).
     * @param minorEventType
     *            The minor type of the event (use of enums is encouraged).
     * @param details
     *            Optional details for the event.
     * 
     * @todo consider passing along the {@link UUID} of the parent event but
     *       then must correlate that {@link UUID} when the event is received.
     */
    protected Event(final IBigdataFederation fed, final EventResource resource,
            final Object majorEventType, final Object minorEventType,
            final Map details) {

        if (fed == null)
            throw new IllegalArgumentException();

        if (resource == null)
            throw new IllegalArgumentException();

        if (majorEventType == null)
            throw new IllegalArgumentException();

        if (minorEventType == null)
            throw new IllegalArgumentException();

        this.fed = fed;

        this.eventUUID = UUID.randomUUID();

        this.hostname = AbstractStatisticsCollector.fullyQualifiedHostName;

        this.serviceIface = this.fed.getServiceIface();

        this.serviceName = this.fed.getServiceName();

        this.serviceUUID = this.fed.getServiceUUID();

        this.resource = resource;
        
        this.majorEventType = majorEventType;

        this.minorEventType = minorEventType;

        // MAY be null.
        if (details != null) {

            addDetails(details);

        }
        
        /*
         * @todo Assignment should be based on a federation configuration
         * option.
         * 
         * if global timestamps, then assign startTime.
         * 
         * if auto-assign, then assigned by the recipient.
         * 
         * if local-assign, then use System.currentTimeMillis().
         */

    }

    /**
     * A child event (major type is the type of the parent).
     * 
     * @param minorEventType
     * 
     * @return The sub-event.
     */
    public Event newSubEvent(final Object minorEventType) {

        return new Event(fed, this.resource, this.majorEventType,
                minorEventType, this.details);
        
    }

    /**
     * A child event (major type is the type of the parent).
     * 
     * @param minorEventType
     * 
     * @param details
     *            Optional ordered (name,value) array.
     * 
     * @return The sub-event.
     */
    public Event newSubEvent(final Object minorEventType,
            final Map details) {

        final Event e = newSubEvent(minorEventType);
        
        if( details != null ) {
            
            e.addDetails(details);
            
        }
        
//        final Map m;
//        if (this.details == null && details == null) {
//            m = null;
//        } else {
//            m = new HashMap();
//            if (this.details != null) {
//                // details from the parent event.
//                m.putAll(this.details);
//            }
//            if (details != null) {
//                // given details.
//                m.putAll(details);
//            }
//        }

        return e;
        
    }

    /**
     * Send the start event.
     * 

* Note: If you want to report an event with a duration then you MUST use * {@link #start()} when the event starts and {@link #end()} when the event * ends. If you want to report an "instantaneous" event then you can just use * {@link #end()}. */ synchronized public Event start() { if (startTime != 0) { // start already reported. throw new IllegalStateException(); } // assign start time. startTime = System.currentTimeMillis(); try { sendEvent(); } catch (Throwable t) { log.warn(t); } return this; } /** * Sends the end event. *

* Note: You can use this method for "instantaneous" events. * * @return The event. */ synchronized public Event end() { if (complete) { throw new IllegalStateException(); } complete = true; endTime = System.currentTimeMillis(); if (startTime == 0L) { // an "instantenous" event. startTime = endTime; } try { sendEvent(); } catch (Throwable t) { log.warn(t); } return this; } /** * Dispatch the {@link Event} via * {@link AbstractFederation#sendEvent(Event)}. */ protected void sendEvent() throws IOException { if(fed instanceof AbstractFederation) { ((AbstractFederation) fed).sendEvent(this); } else { if (log.isInfoEnabled()) log.info(this); } } /** * A header that can be used to interpret the output of {@link #toString()} * (with newline). */ static public String getHeader() { return "eventUUID" + "\tindexName" + "\tpartitionId" + "\tfile" + "\tmajorEventType" + "\tmajorEventValue" + "\tminorEventType" + "\tminorEventValue" + "\tstartTime" + "\tendTime" + "\telapsed" + "\tcomplete" + "\thostname" + "\tserviceIface" + "\tserviceName" + "\tserviceUUID" + "\tdetails" + "\n" ; } /** * Tab-delimited format (with newline). The details columns are written in a * dense format where each (name,value) pair is expressed as * name=value, so only the non-null columns are written out. */ public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(eventUUID); sb.append('\t'); sb.append(resource.indexName); sb.append('\t'); sb.append(resource.partitionId); sb.append('\t'); sb.append(resource.file); sb.append('\t'); sb.append(majorEventType.getClass().getName()); sb.append('\t'); sb.append(majorEventType); sb.append('\t'); sb.append(minorEventType.getClass().getName()); sb.append('\t'); sb.append(minorEventType); sb.append('\t'); sb.append(startTime); sb.append('\t'); sb.append(endTime); sb.append('\t'); if (complete) { sb.append(endTime - startTime); sb.append('\t'); } else { sb.append('\t'); } sb.append(complete); sb.append('\t'); sb.append(hostname); sb.append('\t'); sb.append(serviceIface.getName()); sb.append('\t'); sb.append(serviceName); sb.append('\t'); sb.append(serviceUUID); if (details != null) { final String[] keys = details.keySet().toArray(new String[]{}); Arrays.sort(keys); for (String s : keys) { sb.append('\t'); sb.append(s); sb.append("="); sb.append("" + details.get(s)); } // for (Map.Entry entry : details.entrySet()) { // sb.append('\t'); // sb.append(entry.getKey()); // sb.append("="); // sb.append("" + entry.getValue()); // } } sb.append('\n'); return sb.toString(); } // /** // * A class that knows how to render events in a stable tab-delimited format. // * Known detail columns are predeclared and will be written out in the order // * in which they were declared. Additional detail columns are recognized the // * first time they are used and will always be written out at the column // * index that they were assigned. This makes it possible to reimport the // * events data into a worksheet for processing. // * // * @author Bryan Thompson // * @version $Id$ // */ // public static class EventRenderer { // // public void declareColumn(String) { // // } // public String render(Event e) { // // } // // } // // /** // * Class which parses events written by the {@link EventRenderer}. // * // * @todo we can't really reverse the {@link EventRenderer} when there are // * columns that are not predeclared since their values will never be // * written out. // * // * @author Bryan Thompson // * @version $Id$ // */ // public static class EventParser { // // public Event parseEvent(String s) { // // } // // } /** * Construct an event from the tab-delimited serialization produced from * {@link #toString()}. * * @param s * the tab delimited serialization * * @throws ClassNotFoundException * if any fields specify an invalid classname */ protected Event(final String s) throws ClassNotFoundException { // System.err.println(s); final MyStringTokenizer st = new MyStringTokenizer(s, "\t"); try { this.eventUUID = UUID.fromString(st.nextToken()); // System.err.println("eventUUID: <"+eventUUID+">"); String resourceIndexName = st.nextToken(); // System.err.println("resource.indexName: <"+resourceIndexName+">"); String resourcePartitionId = st.nextToken(); // System.err.println("resource.partitionId: <"+resourcePartitionId+">"); String resourceFile = st.nextToken(); // System.err.println("resource.file: <"+resourceFile+">"); this.resource = new EventResource(resourceIndexName, resourcePartitionId, resourceFile); String majorEventClass = st.nextToken(); // System.err.println("majorEventClass: <"+majorEventClass+">"); if (majorEventClass != null) { Class majorEventType = Class.forName(majorEventClass); String majorEventValue = st.nextToken(); // System.err.println("majorEventValue: <"+majorEventValue+">"); if (majorEventValue == null) { majorEventValue = ""; } if (majorEventType.equals(String.class)) { this.majorEventType = majorEventValue; } else if (majorEventType.isEnum()) { this.majorEventType = Enum.valueOf(majorEventType, majorEventValue); } else { throw new UnsupportedOperationException(); } } else { this.majorEventType = ""; } String minorEventClass = st.nextToken(); // System.err.println("minorEventClass: <"+minorEventClass+">"); if (minorEventClass != null) { Class minorEventType = Class.forName(minorEventClass); String minorEventValue = st.nextToken(); // System.err.println("minorEventValue: <"+minorEventValue+">"); if (minorEventValue == null) { minorEventValue = ""; } if (minorEventType.equals(String.class)) { this.minorEventType = minorEventValue; } else if (minorEventType.isEnum()) { this.minorEventType = Enum.valueOf(minorEventType, minorEventValue); } else { throw new UnsupportedOperationException(); } } else { this.minorEventType = ""; } String startTime = st.nextToken(); // System.err.println("startTime: <"+startTime+">"); this.startTime = Long.valueOf(startTime); String endTime = st.nextToken(); // System.err.println("endTime: <"+endTime+">"); this.endTime = Long.valueOf(endTime); // do nothing with this one String elapsed = st.nextToken(); this.complete = Boolean.valueOf(st.nextToken()); this.hostname = st.nextToken(); String serviceIfaceName = st.nextToken(); this.serviceIface = Class.forName(serviceIfaceName); this.serviceName = st.nextToken(); this.serviceUUID = UUID.fromString(st.nextToken()); this.details = newDetails(); while(st.hasMoreTokens()) { final String t = st.nextToken(); final int pos = t.indexOf('='); final String name = t.substring(0,pos); final String value = t.substring(pos+1,t.length()); this.details.put(name,value); } } catch(Throwable t) { throw new RuntimeException("At field: " + st.getCurrentFieldIndex() + " of " + st.getFieldCount() + " : " + t, t); } } /** * Reconstruct an event object from a string. Useful for post-mortem * analysis. * * @param s the tab-delimited format create by {@link #toString()}. * @return a new event object * @throws ClassNotFoundException * if any CSV fields specify an invalid classname */ public static Event fromString(String s) throws ClassNotFoundException { return new Event(s); } /** * Dissimilar from the regular StringTokenizer, which is cannot handle * empty fields. * * @author Mike Personick */ private static class MyStringTokenizer { private final List tokens; private final String delim; private int i; public MyStringTokenizer(final String s, final String delim) { this.tokens = new ArrayList(20); final StringTokenizer st = new StringTokenizer(s, delim, true); while (st.hasMoreTokens()) { tokens.add(st.nextToken()); } this.delim = delim; this.i = 0; } /** * The #of fields that were extracted. */ public int getFieldCount() { return tokens.size(); } /** * The index of the last field returned in [0:nfields-1] */ public int getCurrentFieldIndex() { return i; } public boolean hasMoreTokens() { return i < tokens.size() && !(i == tokens.size()-1 && delim.contains(tokens.get(tokens.size()-1))); } public String nextToken() { String token = tokens.get(i++); if (delim.contains(token)) { // handles a blank column token = tokens.get(i); if (delim.contains(token)) { token = BLANK; } else { i++; } } if (token != null) { token = token.trim(); if (token.length() == 0) { // handles a column of whitespace. token = BLANK; } } return token; } } static final String BLANK = ""; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy