Home | History | Annotate | Download | only in dtrace
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  *
     26  * ident	"%Z%%M%	%I%	%E% SMI"
     27  */
     28 package org.opensolaris.os.dtrace;
     29 
     30 import java.util.*;
     31 import java.io.*;
     32 import java.beans.*;
     33 
     34 /**
     35  * A single key-value pair in a DTrace aggregation.
     36  * <p>
     37  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
     38  *
     39  * @see Aggregation
     40  * @author Tom Erickson
     41  */
     42 public final class AggregationRecord implements Record, Serializable {
     43     static final long serialVersionUID = -4367614268579702616L;
     44 
     45     static {
     46 	try {
     47 	    BeanInfo info = Introspector.getBeanInfo(AggregationRecord.class);
     48 	    PersistenceDelegate persistenceDelegate =
     49 		    new DefaultPersistenceDelegate(
     50 		    new String[] {"tuple", "value", "ordinal"})
     51 	    {
     52 		/*
     53 		 * Need to prevent DefaultPersistenceDelegate from using
     54 		 * overridden equals() method, resulting in a
     55 		 * StackOverFlowError.  Revert to PersistenceDelegate
     56 		 * implementation.  See
     57 		 * http://forum.java.sun.com/thread.jspa?threadID=
     58 		 * 477019&tstart=135
     59 		 */
     60 		protected boolean
     61 		mutatesTo(Object oldInstance, Object newInstance)
     62 		{
     63 		    return (newInstance != null && oldInstance != null &&
     64 			    oldInstance.getClass() == newInstance.getClass());
     65 		}
     66 	    };
     67 	    BeanDescriptor d = info.getBeanDescriptor();
     68 	    d.setValue("persistenceDelegate", persistenceDelegate);
     69 	} catch (IntrospectionException e) {
     70 	    System.out.println(e);
     71 	}
     72     }
     73 
     74     /** @serial */
     75     private Tuple tuple;
     76     /** @serial */
     77     private AggregationValue value;
     78     /** @serial */
     79     private int ordinal;
     80 
     81 
     82     /**
     83      * Creates an aggregation record with the given key and value.
     84      * Supports XML persistence.
     85      *
     86      * @see #AggregationRecord(Tuple tupleKey, AggregationValue
     87      * recordValue, int n)
     88      */
     89     public
     90     AggregationRecord(Tuple tupleKey, AggregationValue recordValue)
     91     {
     92     //
     93     // Called by native code, but public to support backwardly
     94     // compatible XML encoding.
     95     //
     96 	tuple = tupleKey;
     97 	value = recordValue;
     98 	validate();
     99     }
    100 
    101     /**
    102      * Creates an aggregation record with the given key, value, and
    103      * ordinal. Supports XML persistence.
    104      *
    105      * @param tupleKey aggregation tuple, may be empty (see {@link
    106      * Tuple#EMPTY}) to indicate that this record's value belongs to an
    107      * unkeyed aggregation declared without square brackets, for
    108      * example: <pre>		{@code @a = count();}</pre>
    109      * @param recordValue aggregated value associated with the given
    110      * tuple
    111      * @param n ordinal from zero (first) to n-1 (last) within the
    112      * {@link Aggregate} containing this record
    113      * @throws NullPointerException if the given key or value is
    114      * {@code null}
    115      * @throws IllegalArgumentException if the given ordinal is negative
    116      */
    117     public
    118     AggregationRecord(Tuple tupleKey, AggregationValue recordValue, int n)
    119     {
    120 	tuple = tupleKey;
    121 	value = recordValue;
    122 	ordinal = n;
    123 	validate();
    124     }
    125 
    126     private final void
    127     validate()
    128     {
    129 	if (tuple == null) {
    130 	    throw new NullPointerException("key is null");
    131 	}
    132 	if (value == null) {
    133 	    throw new NullPointerException("value is null");
    134 	}
    135 	if (ordinal < 0) {
    136 	    throw new IllegalArgumentException("ordinal is negative");
    137 	}
    138     }
    139 
    140     /**
    141      * Gets the multi-element key associated with {@link
    142      * #getValue()}.
    143      *
    144      * @return non-null, possibly empty tuple
    145      * @see Aggregation#getRecord(Tuple key)
    146      */
    147     public Tuple
    148     getTuple()
    149     {
    150 	return tuple;
    151     }
    152 
    153     /**
    154      * Gets the value associated with {@link #getTuple()}.  Values
    155      * generated by the DTrace actions {@code count()}, {@code sum()},
    156      * {@code avg()}, {@code min()}, and {@code max()} are of type
    157      * {@link Long}.  Values generated by the DTrace actions {@code
    158      * quantize(}) and {@code lquantize()} are of type {@link
    159      * Distribution}.
    160      *
    161      * @return non-null value keyed to {@link #getTuple()}
    162      */
    163     public AggregationValue
    164     getValue()
    165     {
    166 	return value;
    167     }
    168 
    169     /**
    170      * Gets the ordinal generated when this AggregationRecord was added
    171      * to its containing {@link Aggregate} by the native DTrace library,
    172      * from zero (first) to n-1 (last). The sequence described by an
    173      * aggregate's record ordinals reflects the setting of the {@link
    174      * Option#aggsortkey aggsortkey}, {@link Option#aggsortkeypos
    175      * aggsortkeypos}, {@link Option#aggsortpos aggsortpos}, and {@link
    176      * Option#aggsortrev aggsortrev} DTrace options and matches the way
    177      * that the records would be ordered by {@code dtrace(1M)}.
    178      *
    179      * @return non-negative ordinal from zero (first) to n-1 (last)
    180      * within the {@code Aggregate} containing this record
    181      * @see Aggregate#getOrderedRecords()
    182      */
    183     public int
    184     getOrdinal()
    185     {
    186 	return ordinal;
    187     }
    188 
    189     /**
    190      * Package level access; called by Aggregate
    191      */
    192     void
    193     setOrdinal(int n)
    194     {
    195 	if (n < 0) {
    196 	    throw new IllegalArgumentException("ordinal is negative");
    197 	}
    198 	ordinal = n;
    199     }
    200 
    201     /**
    202      * Compares the specified object with this aggregation record for
    203      * equality.  Defines equality as having the same tuple and value.
    204      *
    205      * @return {@code true} if and only if the specified object is an
    206      * {@code AggregationRecord} and both records have equal tuples as
    207      * defined by {@link Tuple#equals(Object o)} and equal values as
    208      * defined by the implementation of {@link AggregationValue}
    209      */
    210     public boolean
    211     equals(Object o)
    212     {
    213 	if (o instanceof AggregationRecord) {
    214 	    AggregationRecord r = (AggregationRecord)o;
    215 	    return (tuple.equals(r.tuple) &&
    216 		    value.equals(r.value));
    217 	}
    218 	return false;
    219     }
    220 
    221     /**
    222      * Overridden to ensure that equal records have equal hash codes.
    223      */
    224     @Override
    225     public int
    226     hashCode()
    227     {
    228 	int hash = 17;
    229 	hash = (37 * hash) + tuple.hashCode();
    230 	hash = (37 * hash) + value.hashCode();
    231 	return hash;
    232     }
    233 
    234     private void
    235     readObject(ObjectInputStream s)
    236             throws IOException, ClassNotFoundException
    237     {
    238 	s.defaultReadObject();
    239 	// Check class invariants
    240 	try {
    241 	    validate();
    242 	} catch (Exception e) {
    243 	    InvalidObjectException x = new InvalidObjectException(
    244 		    e.getMessage());
    245 	    x.initCause(e);
    246 	    throw x;
    247 	}
    248     }
    249 
    250     /**
    251      * Gets a string representation of this aggregation record useful
    252      * for logging and not intended for display.  The exact details of
    253      * the representation are unspecified and subject to change, but the
    254      * following format may be regarded as typical:
    255      * <pre><code>
    256      * class-name[property1 = value1, property2 = value2]
    257      * </code></pre>
    258      */
    259     @Override
    260     public String
    261     toString()
    262     {
    263 	StringBuilder buf = new StringBuilder();
    264 	buf.append(AggregationRecord.class.getName());
    265 	buf.append("[tuple = ");
    266 	buf.append(tuple);
    267 	buf.append(", value = ");
    268 	buf.append(value);
    269 	buf.append(']');
    270 	return buf.toString();
    271     }
    272 }
    273