/** * ICE Futures, US */ package org.helios.apmrouter.metric.catalog.heap; import org.helios.apmrouter.metric.AgentIdentity; import org.helios.apmrouter.metric.ICEMetricValue; import org.helios.apmrouter.metric.MetricType; import org.helios.apmrouter.util.StringHelper; import java.util.ArrayList; import java.util.List; import static org.helios.apmrouter.util.Methods.nvl; /** * <p>Title: CoreICEMetric</p> * <p>Description: Core generic metricId. One instance exists of this class per metricId name.</p> * <p>Company: ICE Futures US</p> * @author Whitehead (nicholas.whitehead@theice.com) * @version $LastChangedRevision$ * <p><code>org.helios.apmrouter.metric.CoreICEMetric</code></p> */ class CoreICEMetric { // =================================================================== // These attributes are FINAL. // =================================================================== /** The host name */ protected final String host; /** The agent name */ protected final String agent; /** The metricId namespace */ protected final String[] namespace; /** The metricId name */ protected final String name; /** The metricId type */ protected final MetricType type; /** Indicates if this is a flat or mapped metricId name */ protected final boolean flat; // =================================================================== // These attributes can be modified through the UPDATER. // =================================================================== /** The token substitution ID of the metricId */ protected long id = -1; /** The value for this metricId */ protected ICEMetricValue value; // =================================================================== // Contants // =================================================================== /** The namespace delimiter */ static final String NSDELIM = "/"; /** The name delimiter */ static final String NADELIM = ":"; /** The timestamp start delimiter */ static final String TS_S_DELIM = "["; /** The timestamp end delimiter */ static final String TS_E_DELIM = "]"; /** The value delimiter */ static final String VDELIM = "/"; /** The mapped namespace pair delimiter */ static final String MDELIM = "="; /** The format for rendering a transmittable metricId */ static final String TX_FORMAT = TS_S_DELIM + "%s" + TS_E_DELIM + "%s" + NSDELIM + "%s%s" + VDELIM + "%s" ; /** The format for rendering the fully qualified metricId name */ static final String FQN_FORMAT = "%s" + NSDELIM + "%s%s" + NADELIM + "%s" ; /** * Creates a new CoreICEMetric * @param type The metricId type * @param name The metricId name * @param flat Indicates if the metricId namespace is flat or mapped * @param namespace The namespace */ CoreICEMetric(MetricType type, String name, boolean flat, String... namespace) { this.name = name; this.host = AgentIdentity.ID.getHostName(); this.agent = AgentIdentity.ID.getAgentName(); this.namespace = namespace; this.type = type; this.flat = flat; } /** * Returns the host name that this metricId originated from * @return the host name that this metricId originated from */ String getHost() { return host; } /** * The name of the agent that this metricId originated from * @return the name of the agent that this metricId originated from */ String getAgent() { return agent; } /** * Indicates if the metricId namespace is flat or mapped * @return true if the metricId namespace is flat, false if it is mapped */ boolean isFlat() { return flat; } /** * Indicates if the metricId namespace is flat or mapped * @return true if the metricId namespace is mapped, false if it is flat */ boolean isMapped() { return !flat; } /** * The namespace of the metricId * @return the namespace */ String[] getNamespace() { return namespace; } /** * Returns the metricId name * @return the name */ String getName() { return name; } /** * Returns the metricId timestamp or -1 if no timestamp has been set * @return the time */ long getTime() { return value==null ? -1L : value.getTime(); } /** * Returns the metricId type * @return the type */ MetricType getType() { return type; } /** * Returns the fully qualified metricId name * @return the fully qualified metricId name */ String getFQN() { return String.format(FQN_FORMAT, host, agent, getNamespaceF(), name ); } /** * Returns the concatenated namespace * @return the concatenated namespace */ String getNamespaceF() { if(namespace.length==0) return ""; return StringHelper.fastConcatAndDelim(NSDELIM, namespace); } /** * Returns the namespace element at the provided index * @param index The namespace index * @return a namespace element */ String getNamespace(int index) { return namespace[index]; } /** * Returns the number of elements in the namespace * @return the number of elements in the namespace */ int getNameSpaceSize() { return namespace.length; } /** * Creates a new initial ICEMetricBuilder * @param name The metricId name * @param type The metricId type * @param namespace An optional array of namespace entries * @return an ICEMetricBuilder * @throws RuntimeException Thrown if any of the initial metricId parameters are invalid */ static ICEMetricBuilder builder(CharSequence name, MetricType type, String...namespace) { try { return new ICEMetricBuilder(AgentIdentity.ID.getHostName(), AgentIdentity.ID.getAgentName(), nvl(name, "IMetric Name").toString(), type, namespace); } catch (Exception e) { throw new RuntimeException("Failed to create ICEMetricBuilder", e); } } /** * Creates a new initial ICEMetricBuilder * @param name The metricId name * @param type The metricId type name * @param namespace An optional array of namespace entries * @return an ICEMetricBuilder * @throws RuntimeException Thrown if any of the initial metricId parameters are invalid */ static ICEMetricBuilder builder(CharSequence name, CharSequence type, String...namespace) { try { return new ICEMetricBuilder(AgentIdentity.ID.getHostName(), AgentIdentity.ID.getAgentName(), nvl(name, "IMetric Name").toString(), MetricType.valueOfName(type), namespace); } catch (Exception e) { throw new RuntimeException("Failed to create ICEMetricBuilder", e); } } /** * Creates a new initial ICEMetricBuilder * @param name The metricId name * @param type The metricId type ordinal * @param namespace An optional array of namespace entries * @return an ICEMetricBuilder * @throws RuntimeException Thrown if any of the initial metricId parameters are invalid */ static ICEMetricBuilder builder(CharSequence name, int type, String...namespace) { try { return new ICEMetricBuilder(AgentIdentity.ID.getHostName(), AgentIdentity.ID.getAgentName(), nvl(name, "IMetric Name").toString(), MetricType.valueOf(type), namespace); } catch (Exception e) { throw new RuntimeException("Failed to create ICEMetricBuilder", e); } } /** * <p>Title: ICEMetricBuilder</p> * <p>Description: A builder factory for ICEMetrics</p> * <p>Company: ICE Futures US</p> * @author Whitehead (nicholas.whitehead@theice.com) * @version $LastChangedRevision$ * <p><code>org.helios.apmrouter.metric.CoreICEMetric.ICEMetricBuilder</code></p> */ static class ICEMetricBuilder { /** The host name */ protected final String host; /** The agent name */ protected final String agent; /** The metricId namespace */ protected final List<String> namespace = new ArrayList<String>(); /** The metricId name */ protected final String name; /** The metricId type */ protected final MetricType type; /** Indicates if this is a flat or mapped metricId name */ protected Boolean flat = null; /** * Creates a new initial ICEMetricBuilder * @param host The host name * @param agent The agent name * @param name The metricId name * @param type The metricId type * @param namespace An optional array of namespace entries * @throws Exception Thrown if any of the initial metricId parameters are invalid */ ICEMetricBuilder(String host, String agent, String name, MetricType type, String...namespace) throws Exception { this.host = nvl(host, "Host Name"); this.agent = nvl(agent, "Agent Name"); this.name = nvl(name, "IMetric Name"); this.type = nvl(type, "IMetric Type"); if(namespace!=null && namespace.length>0) { for(String ns: namespace) { addNamespace(ns); } } } /** * Builds the CoreICEMetric * @return the CoreICEMetric */ CoreICEMetric build() { return new CoreICEMetric(type, name, flat==null ? true : flat, namespace.toArray(new String[namespace.size()])); } /** * Adds a namespace to the metricId * Throws a runtime exception if any of the namespaces are invalid * @param namespace An array of namespaces to add * @return this builder */ ICEMetricBuilder ns(String...namespace) { try { if(namespace!=null && namespace.length>0) { for(String ns: namespace) { addNamespace(ns); } } return this; } catch (Exception e) { throw new RuntimeException("Namespace element was invalid", e); } } /** * Adds a new mapped namespace * Throws a runtime exception if the namespaces are invalid * @param key The mapped namespace key * @param value The mapped namespace key * @return this builder */ ICEMetricBuilder mns(String key, String value) { try { nvl(key, "Namespace Key"); nvl(value, "Namespace Value"); addNamespace(new StringBuilder(key).append(MDELIM).append(value).toString()); return this; } catch (Exception e) { throw new RuntimeException("Mapped Namespace element was invalid", e); } } /** * Processes a namespace addition. * If the passed ns is null or empty, it is ignored. * If the ns is flat, but the builder has already marked the metricId as mapped, the entry will be merged by removing the MDELIM. * If the ns is mapped, but the builder has already marked the metricId as flat, an exception is thrown. * @param ns The namespace to add * @throws Exception Thrown if the namespace is not null or empty, but is somehow invalid */ private void addNamespace(String ns) throws Exception { if(ns==null) return; ns = ns.trim(); if(ns.isEmpty()) return; if(ns.indexOf(" ")!=-1) ns = ns.replace(" ", ""); int mindex = ns.indexOf(MDELIM); boolean _flat = mindex==-1; if(flat!=null) { // The metricId flattness has already been assigned if(_flat != flat) { // This namespace entry has a different flatness from the metricId if(flat) { // Merge mapped to flat ns = merge(ns, mindex); } else { // Exception: IMetric is mapped but namespace entry was flat throw new Exception("The supplied namespace [" + ns + "] is flat but being added to a metricId name which is mapped " + namespace.toString(), new Throwable()); } } } else { // Flatness of the metricId was not already set, so set it flat = _flat; } // Validate the key/value pair if the type is mapped if(!_flat) validateMapped(ns, mindex); // Add the namespace namespace.add(ns); } /** * Merges a mapped namespace entry into a flat namespace. * If either the key or the value are empty, throws an exceptiom * @param ns The namespace to merge * @param index The index of the MDELIM * @return the merged namespace */ private String merge(String ns, int index) { return new StringBuilder(ns.substring(0, index)).append(ns.substring(index-1)).toString(); } /** * Validates a mapped namespace entry * If either the key or the value are empty, throws an exceptiom * @param ns The namespace to validate * @param index The index of the MDELIM * @throws Exception Thrown if the key or the value is empty */ private void validateMapped(String ns, int index) throws Exception { if(ns.substring(0, index).isEmpty() || ns.substring(index-1).isEmpty()) { throw new Exception("Mapped namespace entry [" + ns + "] had empty key or value", new Throwable()); } } // String a = "foo=bar"; // int index = a.indexOf("="); // a1 = a.substring(0, index); // a2 = a.substring(index+1); // println "[${a1}]/[${a2}]"; } }