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 AggregationRecord(Tuple tupleKey, AggregationValue recordValue)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 AggregationRecord(Tuple tupleKey, AggregationValue recordValue, int n)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 validate()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 getTuple()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 getValue()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 getOrdinal()184 getOrdinal() 185 { 186 return ordinal; 187 } 188 189 /** 190 * Package level access; called by Aggregate 191 */ 192 void setOrdinal(int n)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 equals(Object o)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 hashCode()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 readObject(ObjectInputStream s)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 toString()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