/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, 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.metric; import org.helios.apmrouter.metric.catalog.ICEMetricCatalog; import org.helios.apmrouter.metric.catalog.IDelegateMetric; import org.helios.apmrouter.trace.TXContext; import org.helios.apmrouter.util.StringHelper; import java.nio.ByteBuffer; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import static org.helios.apmrouter.util.Methods.nvl; /** * <p>Title: ICEMetric</p> * <p>Description: The public metricId implementation. ICEMetric wraps one {@link IDelegateMetric} and one {link ICEMetricValue}. One instance is created per trace operation.</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.metric.ICEMetric</code></p> */ public class ICEMetric implements IMetric, Comparable<ICEMetric> { /** The value for this metricId */ protected final ICEMetricValue value; /** The metricId name that this instance represents*/ protected IDelegateMetric metricId; /** The attached TXContext */ protected TXContext txContext; /** The longHashCode of the FQN */ protected long longHashCode = Long.MIN_VALUE; /** Temp token if delegate lookup cache missed */ protected transient long token = -1L; /** * Returns the longHashCode of the FQN * @return the longHashCode of the FQN */ @Override public long getLongHashCode() { if(longHashCode==Long.MIN_VALUE) { longHashCode = longHashCode(getFQN()); } return longHashCode; } /** * Returns the metric ID. * @return the metric ID. */ @Override public IDelegateMetric getMetricId() { return metricId; } /** The unmapped version of this metric */ protected ICEMetric unmapped = null; /** * Calculates a low collision hash code for the passed string * @param s The string to calculate the hash code for * @return the long hashcode */ public static long longHashCode(String s) { long h = 0; int len = s.length(); int off = 0; int hashPrime = s.hashCode(); char val[] = s.toCharArray(); for (int i = 0; i < len; i++) { h = (31*h + val[off++] + (hashPrime*h)); } return h; } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#conflate(org.helios.apmrouter.metric.IMetric) */ @Override public void conflate(IMetric metric) { this.value.conflate(((ICEMetric)metric).value); } /** * Creates a new ICEMetric * @param timestamp THe metric value timestamp * @param value The long value of the metric * @param type The type of the metric * @param dmetric The metric ID * @return a new ICEMetric */ public static ICEMetric newMetric(long timestamp, long value, MetricType type, IDelegateMetric dmetric) { return new ICEMetric(new ICEMetricValue(type, value, timestamp), dmetric); } /** * Creates a new ICEMetric * @param timestamp THe metric value timestamp * @param value The long value of the metric * @param type The type of the metric * @param dmetric The metric ID * @param token Supplementary token in case the delegate metric lookup cache missed * @return a new ICEMetric */ public static ICEMetric newMetric(long timestamp, long value, MetricType type, IDelegateMetric dmetric, long token) { ICEMetric imetric = new ICEMetric(new ICEMetricValue(type, value, timestamp), dmetric); if(dmetric==null) { imetric.token = token; } return imetric; } /** * Creates a new ICEMetric * @param timestamp The metric value timestamp * @param value The non-long value of the metric * @param type The type of the metric * @param dmetric The metric ID * @return a new ICEMetric */ public static ICEMetric newMetric(long timestamp, ByteBuffer value, MetricType type, IDelegateMetric dmetric) { return new ICEMetric(new ICEMetricValue(type, value, timestamp), dmetric); } /** * Creates a new ICEMetric * @param timestamp The metric value timestamp * @param value The non-long value of the metric * @param type The type of the metric * @param dmetric The metric ID * @param token Supplementary token in case the delegate metric lookup failed * @return a new ICEMetric */ public static ICEMetric newMetric(long timestamp, ByteBuffer value, MetricType type, IDelegateMetric dmetric, long token) { ICEMetric imetric = new ICEMetric(new ICEMetricValue(type, value, timestamp), dmetric); if(dmetric==null) { imetric.token = token; } return imetric; } protected void cleanNamespace(CharSequence[] ns) { if(ns==null || ns.length<1) return; for(int i = 0; i < ns.length; i++) { if(ns[i].toString().indexOf("\\/")!=-1) { } } } /** * Attaches a TXContext * @param txContext The context to attach */ public void attachTXContext(TXContext txContext) { this.txContext = txContext; } /** * Checks the TXContext and attaches it if one exists * @return this ICEMetric */ public ICEMetric attachTXContext() { if(TXContext.hasContext()) { attachTXContext(TXContext.rollContext()); } return this; } /** * Creates a new ICEMetric * @param value The value for this metricId * @param metricId The metricId identifier */ ICEMetric(ICEMetricValue value, IDelegateMetric metricId) { this.value = value; this.metricId = metricId; } /** * Creates a new ICEMetric * @param value The metric value * @param host The host name * @param agent The agent name * @param name The metric name * @param type The metric type * @param namespace An optional array of namespace entries * @return an ICEMetric * @throws RuntimeException Thrown if any of the initial metricId parameters are invalid */ public static ICEMetric trace(Object value, String host, String agent, CharSequence name, MetricType type, CharSequence...namespace) { try { return new ICEMetric( type.write(value), ICEMetricCatalog.getInstance().get(host, agent, name, type, namespace) ).attachTXContext(); } catch (Exception e) { throw new RuntimeException("Failed to create ICEMetricBuilder", e); } } /** * Creates a new ICEMetric * @param value The metric value * @param host The host name * @param agent The agent name * @param name The metric name * @param type The metric type * @param namespace An optional array of namespace entries * @return an ICEMetric * @throws RuntimeException Thrown if any of the initial metricId parameters are invalid */ public static ICEMetric trace(Number value, String host, String agent, CharSequence name, MetricType type, CharSequence...namespace) { try { return new ICEMetric( new ICEMetricValue(type, value.longValue()), ICEMetricCatalog.getInstance().get(host, agent, name, type, namespace) ).attachTXContext(); } catch (Exception e) { throw new RuntimeException("Failed to create ICEMetricBuilder", e); } } /** * Creates a new ICEMetric * @param value The metric value * @param host The host name * @param agent The agent name * @param name The metric name * @param type The metric type * @param namespace An optional array of namespace entries * @return an ICEMetric * @throws RuntimeException Thrown if any of the initial metricId parameters are invalid */ public static ICEMetric trace(long value, String host, String agent, CharSequence name, MetricType type, CharSequence...namespace) { try { return new ICEMetric( new ICEMetricValue(type, value), ICEMetricCatalog.getInstance().get(host, agent, name, type, namespace) ).attachTXContext(); } catch (Exception e) { throw new RuntimeException("Failed to create ICEMetricBuilder", e); } } /** * Returns the value object for this metricId * @return the value */ ICEMetricValue getICEMetricValue() { return value; } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getHost() */ @Override public String getHost() { return metricId.getHost(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getAgent() */ @Override public String getAgent() { return metricId.getAgent(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#isFlat() */ @Override public boolean isFlat() { return metricId.isFlat(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#isMapped() */ @Override public boolean isMapped() { return metricId.isMapped(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getNamespace() */ @Override public String[] getNamespace() { return metricId.getNamespace(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getName() */ @Override public String getName() { return metricId.getName(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getTime() */ @Override public long getTime() { return value.getTime(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getType() */ @Override public MetricType getType() { return value.getType(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getFQN() */ @Override public String getFQN() { return String.format(FQN_FORMAT, getHost(), getAgent(), getNamespaceF(), getName()); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getNamespaceF() */ @Override public String getNamespaceF() { String[] ns = getNamespace(); if(ns.length==0) return ""; return StringHelper.fastConcatAndDelim(NSDELIM, ns); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getNamespace(int) */ @Override public String getNamespace(int index) { return getNamespace()[index]; } /** * Returns the namespace element at the provided index. * Throws a RuntimeException if the metric is not mapped * @param index The namespace index * @return a namespace element */ @Override public String getNamespace(CharSequence index) { if(metricId.isFlat()) throw new RuntimeException("Requesting named index namespace on non-mapped metric [" + getFQN() + "]", new Throwable()); String[] ns = metricId.getNamespace(); if(ns==null || ns.length<1) throw new RuntimeException("Requesting named index namespace on zero sized namespace in metric [" + getFQN() + "]", new Throwable()); String key = nvl(index, "Mapped Namespace Index").toString().trim(); for(String s: ns) { int eq = s.indexOf('='); if(key.equals(s.substring(0, eq))) { return s.substring(eq+1); } } return null; } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getNamespaceMap(boolean, boolean) */ @Override public Map<String, String> getNamespaceMap(boolean tagHostAgent, boolean includeTXContext) { if(metricId.isFlat()) throw new RuntimeException("Requesting named index namespace on non-mapped metric [" + getFQN() + "]", new Throwable()); String[] ns = metricId.getNamespace(); if(ns==null || ns.length<1) return Collections.emptyMap(); Map<String, String> map = new LinkedHashMap<String, String>(ns.length + (tagHostAgent ? 2 : 0)); if(tagHostAgent) { map.put(HOST_TAG, metricId.getHost()); map.put(AGENT_TAG, metricId.getAgent()); } if(includeTXContext && txContext!=null) { map.put(TXID_TAG, txContext.toString()); // map.put(TXQ_TAG, "" + txContext.getTxQualifier()); // map.put(TXTHREAD_TAG, "" + txContext.getTxThreadId()); } for(String s: ns) { int eq = s.indexOf('='); map.put(s.substring(0, eq), s.substring(eq+1)); } return map; } /** * Returns the namespace as a map without the host and agent * Throws a RuntimeException if the metric is not mapped * @return a map representing the mapped namespace of this metric */ @Override public Map<String, String> getNamespaceMap() { return getNamespaceMap(false, false); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getNamespaceSize() */ @Override public int getNamespaceSize() { return getNamespace().length; } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getDate() */ @Override public Date getDate() { return value.getDate(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getValue() */ @Override public Object getValue() { if(metricId.getType().isLong()) { return getLongValue(); } return value.getValue(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getLongValue() */ @Override public long getLongValue() { if(!metricId.getType().isLong()) { throw new RuntimeException("The metric [" + getFQN() + "] is not a long type: [" + metricId.getType() + "]", new Throwable()); } return value.getLongValue(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getToken() */ @Override public long getToken() { return metricId!=null ? metricId.getToken() : token; } /** * Sets the metric ID on a token cache miss * @param metricId The metric ID */ public void setMetricId(IDelegateMetric metricId) { if(this.metricId==null) { this.metricId = metricId; } } /** * {@inheritDoc} * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("ICEMetric [>"); builder.append(getType()); builder.append("<"); builder.append(getFQN()); builder.append("["); builder.append(getDate()); builder.append("]:"); builder.append(getValue()); return builder.toString(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getSerSize() */ @Override public int getSerSize() { try { return 1 + // the byteorder byte metricId.getSerSize() +1 +1 + // the metric id size, 8 if tokenized, variable otherwise, +1 for the tokenization indicator byte 8 + // the timestamp size (a long) 1 + // the type size (a byte) (metricId.getType().isLong() ? 8 : // the size of a long value value.getRawValue().limit()+4 // the size of the bytebuffer +4 for the size ) + (hasTXContext() ? TXContext.TXCONTEXT_SIZE : 0); // the size of the TXContext, if attached } catch (Exception e) { throw new RuntimeException("Exception calculating size of metric [" + this + "]", e); } } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getRawValue() */ @Override public ByteBuffer getRawValue() { if(metricId.getType().isLong()) throw new RuntimeException("Call to getRawValue on a long type metric", new Throwable()); return value.getRawValue(); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#hasTXContext() */ @Override public boolean hasTXContext() { return txContext!=null; } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getTXContext() */ @Override public TXContext getTXContext() { return txContext; } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getRoutingKey() */ @Override public CharSequence getRoutingKey() { return String.format("%s-%s", getType().name(), getFQN()); } /** * {@inheritDoc} * @see org.helios.apmrouter.metric.IMetric#getUnmapped() */ @Override public IMetric getUnmapped() { if(isFlat()) return this; if(unmapped==null) { unmapped = new ICEMetric(value, metricId.unmap()); unmapped.txContext = txContext; } return unmapped; } /** * Sorts by timestamp order, oldest first. * {@inheritDoc} * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(ICEMetric other) { return this.value.time<=other.value.time ? -1 : 1; } }