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