/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.trace; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import org.joda.beans.Bean; import org.joda.beans.BeanDefinition; import org.joda.beans.ImmutableBean; import org.joda.beans.ImmutableConstructor; import org.joda.beans.JodaBeanUtils; import org.joda.beans.MetaProperty; import org.joda.beans.Property; import org.joda.beans.PropertyDefinition; import org.joda.beans.impl.direct.DirectFieldsBeanBuilder; import org.joda.beans.impl.direct.DirectMetaBean; import org.joda.beans.impl.direct.DirectMetaProperty; import org.joda.beans.impl.direct.DirectMetaPropertyMap; import org.threeten.bp.Duration; import com.google.common.collect.ImmutableList; /** * A tree tracing a method call and any calls made during its execution. * Contains the execution time, arguments, return values and details of any * exceptions thrown for all the executed methods. The stack trace is not * used in {@link #hashCode()} or {@link #equals(Object)}. */ @BeanDefinition public final class CallGraph implements ImmutableBean { /** The type of the object implementing the called method. */ @PropertyDefinition(validate = "notNull") private final Class<?> _receiverClass; /** The name of the method. */ @PropertyDefinition(validate = "notNull") private final String _methodName; /** The method's parameter types. */ @PropertyDefinition(validate = "notNull") private final List<Class<?>> _parameterTypes; /** * The arguments passed to the method call. Null if we are * only capturing timings. */ @PropertyDefinition private final List<Object> _arguments; /** * The return value from the method call. Null if we are * only capturing timings. */ @PropertyDefinition private final Object _returnValue; /** Throwable thrown when the method was called. */ @PropertyDefinition private final Class<?> _throwableClass; /** Error message of the throwable thrown when the method was called. */ @PropertyDefinition private final String _errorMessage; /** Stack trace of the throwable thrown when the method was called. */ @PropertyDefinition private final String _stackTrace; /** Calls made to other functions during execution of the method. */ @PropertyDefinition(validate = "notNull") private final List<CallGraph> _calls; /** Time taken to execute the called method */ @PropertyDefinition(validate = "notNull") private final Duration _duration; //------------------------------------------------------------------------- /** * Provides a pretty-printed version of the call graph as a string. * * @return a string representation of the call graph, not null */ public String prettyPrint() { return prettyPrint(new StringBuilder(), this, "", "").toString(); } private static StringBuilder prettyPrint(StringBuilder builder, CallGraph trace, String indent, String childIndent) { builder.append('\n').append(indent).append(trace.toString()); for (Iterator<CallGraph> itr = trace.getCalls().iterator(); itr.hasNext(); ) { CallGraph next = itr.next(); String newIndent; String newChildIndent; boolean isFinalChild = !itr.hasNext(); if (!isFinalChild) { newIndent = childIndent + " |--"; newChildIndent = childIndent + " | "; } else { newIndent = childIndent + " `--"; newChildIndent = childIndent + " "; } // these are unicode characters for box drawing /* if (!isFinalChild) { newIndent = childIndent + " \u251c\u2500\u2500"; newChildIndent = childIndent + " \u2502 "; } else { newIndent = childIndent + " \u2514\u2500\u2500"; newChildIndent = childIndent + " "; } */ prettyPrint(builder, next, newIndent, newChildIndent); } return builder; } @Override public String toString() { // We are timings only if (_returnValue == null && _arguments == null) { return _receiverClass.getSimpleName() + "." + _methodName + "()" + " in " + _duration.toNanos() + "ns"; } String errorMessage = _errorMessage == null ? "" : " '" + _errorMessage + "'"; return _receiverClass.getSimpleName() + "." + _methodName + "()" + (_throwableClass == null ? " -> " + _returnValue : " threw " + _throwableClass + errorMessage) + " in " + _duration.toNanos() + "ns" + (_arguments == null ? "" : ", args: " + _arguments); } @ImmutableConstructor CallGraph(Class<?> receiverClass, String methodName, List<Class<?>> parameterTypes, List<Object> arguments, Object returnValue, Class<?> throwableClass, String errorMessage, String stackTrace, List<CallGraph> calls, Duration duration) { JodaBeanUtils.notNull(receiverClass, "receiverClass"); JodaBeanUtils.notNull(methodName, "methodName"); JodaBeanUtils.notNull(parameterTypes, "parameterTypes"); JodaBeanUtils.notNull(calls, "calls"); JodaBeanUtils.notNull(duration, "duration"); _receiverClass = receiverClass; _methodName = methodName; _parameterTypes = ImmutableList.copyOf(parameterTypes); _arguments = arguments == null ? null : ImmutableList.copyOf(arguments); _returnValue = returnValue; _throwableClass = throwableClass; _errorMessage = errorMessage; _stackTrace = stackTrace; _calls = ImmutableList.copyOf(calls); _duration = duration; } @Override public int hashCode() { return Objects.hash(_receiverClass, _methodName, _parameterTypes, _arguments, _returnValue, _throwableClass, _errorMessage, _calls, _duration); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final CallGraph other = (CallGraph) obj; return Objects.equals(this._receiverClass, other._receiverClass) && Objects.equals(this._methodName, other._methodName) && Objects.equals(this._parameterTypes, other._parameterTypes) && Objects.equals(this._arguments, other._arguments) && Objects.equals(this._returnValue, other._returnValue) && Objects.equals(this._throwableClass, other._throwableClass) && Objects.equals(this._errorMessage, other._errorMessage) && Objects.equals(this._calls, other._calls) && Objects.equals(this._duration, other._duration); } //------------------------- AUTOGENERATED START ------------------------- ///CLOVER:OFF /** * The meta-bean for {@code CallGraph}. * @return the meta-bean, not null */ public static CallGraph.Meta meta() { return CallGraph.Meta.INSTANCE; } static { JodaBeanUtils.registerMetaBean(CallGraph.Meta.INSTANCE); } /** * Returns a builder used to create an instance of the bean. * @return the builder, not null */ public static CallGraph.Builder builder() { return new CallGraph.Builder(); } @Override public CallGraph.Meta metaBean() { return CallGraph.Meta.INSTANCE; } @Override public <R> Property<R> property(String propertyName) { return metaBean().<R>metaProperty(propertyName).createProperty(this); } @Override public Set<String> propertyNames() { return metaBean().metaPropertyMap().keySet(); } //----------------------------------------------------------------------- /** * Gets the type of the object implementing the called method. * @return the value of the property, not null */ public Class<?> getReceiverClass() { return _receiverClass; } //----------------------------------------------------------------------- /** * Gets the name of the method. * @return the value of the property, not null */ public String getMethodName() { return _methodName; } //----------------------------------------------------------------------- /** * Gets the method's parameter types. * @return the value of the property, not null */ public List<Class<?>> getParameterTypes() { return _parameterTypes; } //----------------------------------------------------------------------- /** * Gets the arguments passed to the method call. Null if we are * only capturing timings. * @return the value of the property */ public List<Object> getArguments() { return _arguments; } //----------------------------------------------------------------------- /** * Gets the return value from the method call. Null if we are * only capturing timings. * @return the value of the property */ public Object getReturnValue() { return _returnValue; } //----------------------------------------------------------------------- /** * Gets throwable thrown when the method was called. * @return the value of the property */ public Class<?> getThrowableClass() { return _throwableClass; } //----------------------------------------------------------------------- /** * Gets error message of the throwable thrown when the method was called. * @return the value of the property */ public String getErrorMessage() { return _errorMessage; } //----------------------------------------------------------------------- /** * Gets stack trace of the throwable thrown when the method was called. * @return the value of the property */ public String getStackTrace() { return _stackTrace; } //----------------------------------------------------------------------- /** * Gets calls made to other functions during execution of the method. * @return the value of the property, not null */ public List<CallGraph> getCalls() { return _calls; } //----------------------------------------------------------------------- /** * Gets time taken to execute the called method * @return the value of the property, not null */ public Duration getDuration() { return _duration; } //----------------------------------------------------------------------- /** * Returns a builder that allows this bean to be mutated. * @return the mutable builder, not null */ public Builder toBuilder() { return new Builder(this); } //----------------------------------------------------------------------- /** * The meta-bean for {@code CallGraph}. */ public static final class Meta extends DirectMetaBean { /** * The singleton instance of the meta-bean. */ static final Meta INSTANCE = new Meta(); /** * The meta-property for the {@code receiverClass} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<Class<?>> _receiverClass = DirectMetaProperty.ofImmutable( this, "receiverClass", CallGraph.class, (Class) Class.class); /** * The meta-property for the {@code methodName} property. */ private final MetaProperty<String> _methodName = DirectMetaProperty.ofImmutable( this, "methodName", CallGraph.class, String.class); /** * The meta-property for the {@code parameterTypes} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<List<Class<?>>> _parameterTypes = DirectMetaProperty.ofImmutable( this, "parameterTypes", CallGraph.class, (Class) List.class); /** * The meta-property for the {@code arguments} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<List<Object>> _arguments = DirectMetaProperty.ofImmutable( this, "arguments", CallGraph.class, (Class) List.class); /** * The meta-property for the {@code returnValue} property. */ private final MetaProperty<Object> _returnValue = DirectMetaProperty.ofImmutable( this, "returnValue", CallGraph.class, Object.class); /** * The meta-property for the {@code throwableClass} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<Class<?>> _throwableClass = DirectMetaProperty.ofImmutable( this, "throwableClass", CallGraph.class, (Class) Class.class); /** * The meta-property for the {@code errorMessage} property. */ private final MetaProperty<String> _errorMessage = DirectMetaProperty.ofImmutable( this, "errorMessage", CallGraph.class, String.class); /** * The meta-property for the {@code stackTrace} property. */ private final MetaProperty<String> _stackTrace = DirectMetaProperty.ofImmutable( this, "stackTrace", CallGraph.class, String.class); /** * The meta-property for the {@code calls} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<List<CallGraph>> _calls = DirectMetaProperty.ofImmutable( this, "calls", CallGraph.class, (Class) List.class); /** * The meta-property for the {@code duration} property. */ private final MetaProperty<Duration> _duration = DirectMetaProperty.ofImmutable( this, "duration", CallGraph.class, Duration.class); /** * The meta-properties. */ private final Map<String, MetaProperty<?>> _metaPropertyMap$ = new DirectMetaPropertyMap( this, null, "receiverClass", "methodName", "parameterTypes", "arguments", "returnValue", "throwableClass", "errorMessage", "stackTrace", "calls", "duration"); /** * Restricted constructor. */ private Meta() { } @Override protected MetaProperty<?> metaPropertyGet(String propertyName) { switch (propertyName.hashCode()) { case -1946768183: // receiverClass return _receiverClass; case -723163380: // methodName return _methodName; case 1123325520: // parameterTypes return _parameterTypes; case -2035517098: // arguments return _arguments; case -1495129567: // returnValue return _returnValue; case -1147066344: // throwableClass return _throwableClass; case 1203236063: // errorMessage return _errorMessage; case 2026279837: // stackTrace return _stackTrace; case 94425557: // calls return _calls; case -1992012396: // duration return _duration; } return super.metaPropertyGet(propertyName); } @Override public CallGraph.Builder builder() { return new CallGraph.Builder(); } @Override public Class<? extends CallGraph> beanType() { return CallGraph.class; } @Override public Map<String, MetaProperty<?>> metaPropertyMap() { return _metaPropertyMap$; } //----------------------------------------------------------------------- /** * The meta-property for the {@code receiverClass} property. * @return the meta-property, not null */ public MetaProperty<Class<?>> receiverClass() { return _receiverClass; } /** * The meta-property for the {@code methodName} property. * @return the meta-property, not null */ public MetaProperty<String> methodName() { return _methodName; } /** * The meta-property for the {@code parameterTypes} property. * @return the meta-property, not null */ public MetaProperty<List<Class<?>>> parameterTypes() { return _parameterTypes; } /** * The meta-property for the {@code arguments} property. * @return the meta-property, not null */ public MetaProperty<List<Object>> arguments() { return _arguments; } /** * The meta-property for the {@code returnValue} property. * @return the meta-property, not null */ public MetaProperty<Object> returnValue() { return _returnValue; } /** * The meta-property for the {@code throwableClass} property. * @return the meta-property, not null */ public MetaProperty<Class<?>> throwableClass() { return _throwableClass; } /** * The meta-property for the {@code errorMessage} property. * @return the meta-property, not null */ public MetaProperty<String> errorMessage() { return _errorMessage; } /** * The meta-property for the {@code stackTrace} property. * @return the meta-property, not null */ public MetaProperty<String> stackTrace() { return _stackTrace; } /** * The meta-property for the {@code calls} property. * @return the meta-property, not null */ public MetaProperty<List<CallGraph>> calls() { return _calls; } /** * The meta-property for the {@code duration} property. * @return the meta-property, not null */ public MetaProperty<Duration> duration() { return _duration; } //----------------------------------------------------------------------- @Override protected Object propertyGet(Bean bean, String propertyName, boolean quiet) { switch (propertyName.hashCode()) { case -1946768183: // receiverClass return ((CallGraph) bean).getReceiverClass(); case -723163380: // methodName return ((CallGraph) bean).getMethodName(); case 1123325520: // parameterTypes return ((CallGraph) bean).getParameterTypes(); case -2035517098: // arguments return ((CallGraph) bean).getArguments(); case -1495129567: // returnValue return ((CallGraph) bean).getReturnValue(); case -1147066344: // throwableClass return ((CallGraph) bean).getThrowableClass(); case 1203236063: // errorMessage return ((CallGraph) bean).getErrorMessage(); case 2026279837: // stackTrace return ((CallGraph) bean).getStackTrace(); case 94425557: // calls return ((CallGraph) bean).getCalls(); case -1992012396: // duration return ((CallGraph) bean).getDuration(); } return super.propertyGet(bean, propertyName, quiet); } @Override protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) { metaProperty(propertyName); if (quiet) { return; } throw new UnsupportedOperationException("Property cannot be written: " + propertyName); } } //----------------------------------------------------------------------- /** * The bean-builder for {@code CallGraph}. */ public static final class Builder extends DirectFieldsBeanBuilder<CallGraph> { private Class<?> _receiverClass; private String _methodName; private List<Class<?>> _parameterTypes = new ArrayList<Class<?>>(); private List<Object> _arguments; private Object _returnValue; private Class<?> _throwableClass; private String _errorMessage; private String _stackTrace; private List<CallGraph> _calls = new ArrayList<CallGraph>(); private Duration _duration; /** * Restricted constructor. */ private Builder() { } /** * Restricted copy constructor. * @param beanToCopy the bean to copy from, not null */ private Builder(CallGraph beanToCopy) { this._receiverClass = beanToCopy.getReceiverClass(); this._methodName = beanToCopy.getMethodName(); this._parameterTypes = new ArrayList<Class<?>>(beanToCopy.getParameterTypes()); this._arguments = (beanToCopy.getArguments() != null ? new ArrayList<Object>(beanToCopy.getArguments()) : null); this._returnValue = beanToCopy.getReturnValue(); this._throwableClass = beanToCopy.getThrowableClass(); this._errorMessage = beanToCopy.getErrorMessage(); this._stackTrace = beanToCopy.getStackTrace(); this._calls = new ArrayList<CallGraph>(beanToCopy.getCalls()); this._duration = beanToCopy.getDuration(); } //----------------------------------------------------------------------- @Override public Object get(String propertyName) { switch (propertyName.hashCode()) { case -1946768183: // receiverClass return _receiverClass; case -723163380: // methodName return _methodName; case 1123325520: // parameterTypes return _parameterTypes; case -2035517098: // arguments return _arguments; case -1495129567: // returnValue return _returnValue; case -1147066344: // throwableClass return _throwableClass; case 1203236063: // errorMessage return _errorMessage; case 2026279837: // stackTrace return _stackTrace; case 94425557: // calls return _calls; case -1992012396: // duration return _duration; default: throw new NoSuchElementException("Unknown property: " + propertyName); } } @SuppressWarnings("unchecked") @Override public Builder set(String propertyName, Object newValue) { switch (propertyName.hashCode()) { case -1946768183: // receiverClass this._receiverClass = (Class<?>) newValue; break; case -723163380: // methodName this._methodName = (String) newValue; break; case 1123325520: // parameterTypes this._parameterTypes = (List<Class<?>>) newValue; break; case -2035517098: // arguments this._arguments = (List<Object>) newValue; break; case -1495129567: // returnValue this._returnValue = (Object) newValue; break; case -1147066344: // throwableClass this._throwableClass = (Class<?>) newValue; break; case 1203236063: // errorMessage this._errorMessage = (String) newValue; break; case 2026279837: // stackTrace this._stackTrace = (String) newValue; break; case 94425557: // calls this._calls = (List<CallGraph>) newValue; break; case -1992012396: // duration this._duration = (Duration) newValue; break; default: throw new NoSuchElementException("Unknown property: " + propertyName); } return this; } @Override public Builder set(MetaProperty<?> property, Object value) { super.set(property, value); return this; } @Override public Builder setString(String propertyName, String value) { setString(meta().metaProperty(propertyName), value); return this; } @Override public Builder setString(MetaProperty<?> property, String value) { super.setString(property, value); return this; } @Override public Builder setAll(Map<String, ? extends Object> propertyValueMap) { super.setAll(propertyValueMap); return this; } @Override public CallGraph build() { return new CallGraph( _receiverClass, _methodName, _parameterTypes, _arguments, _returnValue, _throwableClass, _errorMessage, _stackTrace, _calls, _duration); } //----------------------------------------------------------------------- /** * Sets the {@code receiverClass} property in the builder. * @param receiverClass the new value, not null * @return this, for chaining, not null */ public Builder receiverClass(Class<?> receiverClass) { JodaBeanUtils.notNull(receiverClass, "receiverClass"); this._receiverClass = receiverClass; return this; } /** * Sets the {@code methodName} property in the builder. * @param methodName the new value, not null * @return this, for chaining, not null */ public Builder methodName(String methodName) { JodaBeanUtils.notNull(methodName, "methodName"); this._methodName = methodName; return this; } /** * Sets the {@code parameterTypes} property in the builder. * @param parameterTypes the new value, not null * @return this, for chaining, not null */ public Builder parameterTypes(List<Class<?>> parameterTypes) { JodaBeanUtils.notNull(parameterTypes, "parameterTypes"); this._parameterTypes = parameterTypes; return this; } /** * Sets the {@code arguments} property in the builder. * @param arguments the new value * @return this, for chaining, not null */ public Builder arguments(List<Object> arguments) { this._arguments = arguments; return this; } /** * Sets the {@code arguments} property in the builder * from an array of objects. * @param arguments the new value * @return this, for chaining, not null */ public Builder arguments(Object... arguments) { return arguments(Arrays.asList(arguments)); } /** * Sets the {@code returnValue} property in the builder. * @param returnValue the new value * @return this, for chaining, not null */ public Builder returnValue(Object returnValue) { this._returnValue = returnValue; return this; } /** * Sets the {@code throwableClass} property in the builder. * @param throwableClass the new value * @return this, for chaining, not null */ public Builder throwableClass(Class<?> throwableClass) { this._throwableClass = throwableClass; return this; } /** * Sets the {@code errorMessage} property in the builder. * @param errorMessage the new value * @return this, for chaining, not null */ public Builder errorMessage(String errorMessage) { this._errorMessage = errorMessage; return this; } /** * Sets the {@code stackTrace} property in the builder. * @param stackTrace the new value * @return this, for chaining, not null */ public Builder stackTrace(String stackTrace) { this._stackTrace = stackTrace; return this; } /** * Sets the {@code calls} property in the builder. * @param calls the new value, not null * @return this, for chaining, not null */ public Builder calls(List<CallGraph> calls) { JodaBeanUtils.notNull(calls, "calls"); this._calls = calls; return this; } /** * Sets the {@code calls} property in the builder * from an array of objects. * @param calls the new value, not null * @return this, for chaining, not null */ public Builder calls(CallGraph... calls) { return calls(Arrays.asList(calls)); } /** * Sets the {@code duration} property in the builder. * @param duration the new value, not null * @return this, for chaining, not null */ public Builder duration(Duration duration) { JodaBeanUtils.notNull(duration, "duration"); this._duration = duration; return this; } //----------------------------------------------------------------------- @Override public String toString() { StringBuilder buf = new StringBuilder(352); buf.append("CallGraph.Builder{"); buf.append("receiverClass").append('=').append(JodaBeanUtils.toString(_receiverClass)).append(',').append(' '); buf.append("methodName").append('=').append(JodaBeanUtils.toString(_methodName)).append(',').append(' '); buf.append("parameterTypes").append('=').append(JodaBeanUtils.toString(_parameterTypes)).append(',').append(' '); buf.append("arguments").append('=').append(JodaBeanUtils.toString(_arguments)).append(',').append(' '); buf.append("returnValue").append('=').append(JodaBeanUtils.toString(_returnValue)).append(',').append(' '); buf.append("throwableClass").append('=').append(JodaBeanUtils.toString(_throwableClass)).append(',').append(' '); buf.append("errorMessage").append('=').append(JodaBeanUtils.toString(_errorMessage)).append(',').append(' '); buf.append("stackTrace").append('=').append(JodaBeanUtils.toString(_stackTrace)).append(',').append(' '); buf.append("calls").append('=').append(JodaBeanUtils.toString(_calls)).append(',').append(' '); buf.append("duration").append('=').append(JodaBeanUtils.toString(_duration)); buf.append('}'); return buf.toString(); } } ///CLOVER:ON //-------------------------- AUTOGENERATED END -------------------------- }