/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2012, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.codahale.annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.yammer.metrics.annotation.Timed;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationDefaultAttribute;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.StringMemberValue;
/**
* <p>Title: TimedImpl</p>
* <p>Description: Instantiation of the {@link Timed} annotation.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.codahale.annotation.TimedImpl</code></p>
*/
public class TimedImpl {
/** The group of the timer. */
protected final String group;
/** The type of the timer. */
protected final String type;
/** The optional scope of the timer. */
protected final String scope;
/** The name of the timer. */
protected final String name;
/** The rate unit of the timer. */
protected final TimeUnit rateUnit;
/** The duration unit of the timer. */
protected final TimeUnit durationUnit;
/** Scoped CtMethod template for a codahale timer */
public static final String SCOPED_TIMER_TEMPLATE = "org.helios.apmrouter.codahale.metrics.Metrics.defaultRegistry().newTimer(%s.class, \"%s\", \"%s\", java.util.concurrent.TimeUnit.%s, java.util.concurrent.TimeUnit.%s);";
/** No Scope CtMethod template for a codahale timer */
public static final String TIMER_TEMPLATE = "org.helios.apmrouter.codahale.metrics.Metrics.defaultRegistry().newTimer(%s.class, \"%s\", java.util.concurrent.TimeUnit.%s, java.util.concurrent.TimeUnit.%s);";
/** A map of named default values since we're having issues getting them from javassist */
public static final Map<String, String> DEFAULT_VALUES;
static {
Map<String, String> tmp = new HashMap<String, String>();
tmp.put("name", "");
tmp.put("group", "");
tmp.put("type", "");
tmp.put("scope", "");
tmp.put("rateUnit", TimeUnit.SECONDS.name());
tmp.put("durationUnit", TimeUnit.MILLISECONDS.name());
DEFAULT_VALUES = Collections.unmodifiableMap(tmp);
}
/**
* Creates a new TimedImpl
* @param group The group of the timer
* @param type The type of the timer
* @param scope The scope of the timer
* @param name The name of the timer
* @param rateUnit The rate unit of the timer
* @param durationUnit The duration unit of the timer
*/
public TimedImpl(String group, String type, String scope, String name, TimeUnit rateUnit, TimeUnit durationUnit) {
this.group = group;
this.type = type;
this.scope = scope;
this.name = name;
this.rateUnit = rateUnit;
this.durationUnit = durationUnit;
}
/**
* Creates a new TimedImpl
* @param name The name of the timer
*/
public TimedImpl(String name) {
this.group = "";
this.type = "";
this.scope = "";
this.name = name;
this.rateUnit = TimeUnit.SECONDS;
this.durationUnit = TimeUnit.MILLISECONDS;
}
/**
* Returns the javassist field initializer string for the timer created for this annotation impl instance
* @param clazzName The class name being instrumented
* @return a javassist field initializer string
*/
public String getTimerInitializer(String clazzName) {
if("".equals(scope)) {
return String.format(TIMER_TEMPLATE, clazzName, name, durationUnit, rateUnit);
}
return String.format(SCOPED_TIMER_TEMPLATE, clazzName, name, scope, durationUnit, rateUnit);
}
/**
* Creates a new TimedImpl from the annotation data extracted from the passed method
* @param method The method that is annotated
* @throws NotFoundException thrown if supporting classes cannot be found
*/
public TimedImpl(CtMethod method) throws NotFoundException {
MethodInfo minfo = method.getMethodInfo();
AnnotationsAttribute attr = (AnnotationsAttribute)minfo.getAttribute(AnnotationsAttribute.visibleTag);
Annotation an = attr.getAnnotation(Timed.class.getName());
this.group = getValueFor("group", an);
this.type = getValueFor("type", an);
this.scope = getValueFor("scope", an);
String _name = getValueFor("name", an);
if("".equals(_name)) {
this.name = method.getName();
} else {
this.name = _name;
}
this.rateUnit = TimeUnit.valueOf(getValueFor("rateUnit", an));
this.durationUnit = TimeUnit.valueOf(getValueFor("durationUnit", an));
}
/**
* Extracts the value or default value for the passed annotation attribute name
* @param name The annotation attribute name
* @param an The annotation
* @return The annotation attribute value as a string
* @throws NotFoundException thrown if supporting classes cannot be found
*/
protected String getValueFor(String name, Annotation an) throws NotFoundException {
MemberValue mv = an.getMemberValue(name);
if(mv==null) {
return DEFAULT_VALUES.get(name);
}
if(mv instanceof StringMemberValue) {
return ((StringMemberValue)mv).getValue();
} else if(mv instanceof EnumMemberValue) {
return ((EnumMemberValue)mv).getValue();
} else {
throw new RuntimeException("Unexpected member value type [" + mv.getClass().getSimpleName() + "]", new Throwable());
}
}
/**
* {@inheritDoc}
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "TimedImpl [group=" + group + ", type=" + type + ", scope="
+ scope + ", name=" + name + ", rateUnit=" + rateUnit
+ ", durationUnit=" + durationUnit + "]";
}
}