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 2006 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 = -8439277589555814411L; 44 45 static { 46 try { 47 BeanInfo info = Introspector.getBeanInfo(AggregationRecord.class); 48 PersistenceDelegate persistenceDelegate = 49 new DefaultPersistenceDelegate( 50 new String[] {"tuple", "value"}) 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 79 /** 80 * Creates an aggregation record with the given key and value. 81 * Supports XML persistence. 82 * 83 * @param tupleKey aggregation tuple, may be empty (see {@link 84 * Tuple#EMPTY}) to indicate that this record's value belongs to an 85 * unkeyed aggregation declared without square brackets, for 86 * example: <pre> {@code @a = count();}</pre> 87 * @param recordValue aggregated value associated with the given 88 * tuple 89 * @throws NullPointerException if the given key or value is 90 * {@code null} 91 */ 92 public 93 AggregationRecord(Tuple tupleKey, AggregationValue recordValue) 94 { 95 tuple = tupleKey; 96 value = recordValue; 97 validate(); 98 } 99 100 private void 101 validate() 102 { 103 if (tuple == null) { 104 throw new NullPointerException("key is null"); 105 } 106 if (value == null) { 107 throw new NullPointerException("value is null"); 108 } 109 } 110 111 /** 112 * Gets the multi-element key associated with {@link 113 * #getValue()}. 114 * 115 * @return non-null, possibly empty tuple 116 * @see Aggregation#getRecord(Tuple key) 117 */ 118 public Tuple 119 getTuple() 120 { 121 return tuple; 122 } 123 124 /** 125 * Gets the value associated with {@link #getTuple()}. Values 126 * generated by the DTrace actions {@code count()}, {@code sum()}, 127 * {@code avg()}, {@code min()}, and {@code max()} are of type 128 * {@link Long}. Values generated by the DTrace actions {@code 129 * quantize(}) and {@code lquantize()} are of type {@link 130 * Distribution}. 131 * 132 * @return non-null value keyed to {@link #getTuple()} 133 */ 134 public AggregationValue 135 getValue() 136 { 137 return value; 138 } 139 140 /** 141 * Compares the specified object with this aggregation record for 142 * equality. Defines equality as having the same tuple and value. 143 * 144 * @return {@code true} if and only if the specified object is an 145 * {@code AggregationRecord} and both records have equal tuples as 146 * defined by {@link Tuple#equals(Object o)} and equal values as 147 * defined by the implementation of {@link AggregationValue} 148 */ 149 public boolean 150 equals(Object o) 151 { 152 if (o instanceof AggregationRecord) { 153 AggregationRecord r = (AggregationRecord)o; 154 return (tuple.equals(r.tuple) && 155 value.equals(r.value)); 156 } 157 return false; 158 } 159 160 /** 161 * Overridden to ensure that equal records have equal hash codes. 162 */ 163 @Override 164 public int 165 hashCode() 166 { 167 int hash = 17; 168 hash = (37 * hash) + tuple.hashCode(); 169 hash = (37 * hash) + value.hashCode(); 170 return hash; 171 } 172 173 private void 174 readObject(ObjectInputStream s) 175 throws IOException, ClassNotFoundException 176 { 177 s.defaultReadObject(); 178 // Check class invariants 179 try { 180 validate(); 181 } catch (Exception e) { 182 throw new InvalidObjectException(e.getMessage()); 183 } 184 } 185 186 /** 187 * Gets a string representation of this aggregation record useful 188 * for logging and not intended for display. The exact details of 189 * the representation are unspecified and subject to change, but the 190 * following format may be regarded as typical: 191 * <pre><code> 192 * class-name[property1 = value1, property2 = value2] 193 * </code></pre> 194 */ 195 @Override 196 public String 197 toString() 198 { 199 StringBuffer buf = new StringBuffer(); 200 buf.append(AggregationRecord.class.getName()); 201 buf.append("[tuple = "); 202 buf.append(tuple); 203 buf.append(", value = "); 204 buf.append(value); 205 buf.append(']'); 206 return buf.toString(); 207 } 208 } 209