001 package org.hackystat.telemetry.analyzer.function; 002 003 import java.io.InputStream; 004 import java.util.TreeMap; 005 import java.util.logging.Logger; 006 007 import javax.xml.bind.JAXBContext; 008 import javax.xml.bind.Unmarshaller; 009 010 import org.hackystat.telemetry.analyzer.function.jaxb.FunctionDefinition; 011 import org.hackystat.telemetry.analyzer.function.jaxb.FunctionDefinitions; 012 import org.hackystat.telemetry.analyzer.model.TelemetryStreamCollection; 013 import org.hackystat.utilities.logger.HackystatLogger; 014 import org.hackystat.utilities.stacktrace.StackTrace; 015 016 /** 017 * Implements a global singleton for managing telemetry function instances. It serves two purposes: 018 * <ul> 019 * <li>Provides a central repository for all available telemetry functions. 020 * <li>Provides a interface for invoking those functions. 021 * </ul> 022 * 023 * All function names are case-insensitive. 024 * 025 * @author (Cedric) Qin ZHANG 026 */ 027 public class TelemetryFunctionManager { 028 029 /** 030 * Built-in functions for telemetry language and evaluator. 031 * Key is function name (lower case), value is TelemetryFunctionInfo instance. 032 */ 033 private TreeMap<String, TelemetryFunctionInfo> functionMap = 034 new TreeMap<String, TelemetryFunctionInfo>(); 035 036 /** The global instance of this manager. */ 037 private static TelemetryFunctionManager theInstance = new TelemetryFunctionManager(); 038 039 /** 040 * Gets the global instance. 041 * 042 * @return The global instance. 043 */ 044 public static TelemetryFunctionManager getInstance() { 045 return theInstance; 046 } 047 048 /** 049 * Defines "built-in" telemetry functions. 050 */ 051 private TelemetryFunctionManager() { 052 Logger logger = HackystatLogger.getLogger("org.hackystat.telemetry"); 053 FunctionDefinitions definitions = null; 054 055 try { 056 logger.info("Loading built-in telemetry function definitions."); 057 //InputStream defStream = getClass().getResourceAsStream("impl/function.definitions.xml"); 058 InputStream defStream = 059 TelemetryFunctionManager.class.getResourceAsStream("impl/function.definitions.xml"); 060 JAXBContext jaxbContext = JAXBContext 061 .newInstance(org.hackystat.telemetry.analyzer.function.jaxb.ObjectFactory.class); 062 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 063 definitions = (FunctionDefinitions) unmarshaller.unmarshal(defStream); 064 } 065 catch (Exception e) { 066 logger.severe("Could not find built-in telemetry function definitions! " 067 + StackTrace.toString(e)); 068 return; 069 } 070 071 for (FunctionDefinition definition : definitions.getFunctionDefinition()) { 072 String name = definition.getName(); 073 try { 074 logger.info("Defining built-in telemetry function " + name); 075 Class<?> clazz = Class.forName(definition.getClassName()); 076 TelemetryFunction telemetryFunc = (TelemetryFunction) clazz.newInstance(); 077 TelemetryFunctionInfo funcInfo = new TelemetryFunctionInfo(telemetryFunc, definition); 078 this.functionMap.put(name.toLowerCase(), funcInfo); 079 } 080 catch (Exception classEx) { 081 logger.severe("Unable to define " 082 + definition.getClassName() + ". Entry ignored. " + classEx.getMessage()); 083 continue; 084 } 085 } 086 } 087 088 /** 089 * Determines whether a particular telemetry function is available. 090 * 091 * @param functionName Telemetry function name. 092 * @return True if the specified telemetry function is available. 093 */ 094 public boolean isFunction(String functionName) { 095 return this.functionMap.containsKey(functionName.toLowerCase()); 096 } 097 098 /** 099 * Gets telemetry function information by name. 100 * 101 * @param functionName The name of the function. 102 * @return The telemetry function information, or null if the function is not defined. 103 */ 104 public TelemetryFunctionInfo getFunctionInfo(String functionName) { 105 return this.functionMap.get(functionName.toLowerCase()); 106 } 107 108 /** 109 * Invokes telemetry function to perform computation. 110 * 111 * @param functionName The name of the telemetry function. 112 * @param parameters An array of objects of type either <code>String</code>, 113 * <code>Number</code>, and/or <code>TelemetryStreamCollection</code>. 114 * 115 * @return Either an instance of <code>Number</code> or <code>TelemetryStreamCollection</code>. 116 * 117 * @throws TelemetryFunctionException If anything is wrong. 118 */ 119 public Object compute(String functionName, Object[] parameters) 120 throws TelemetryFunctionException { 121 122 TelemetryFunctionInfo functionInfo = this.getFunctionInfo(functionName.toLowerCase()); 123 if (functionInfo == null) { 124 throw new TelemetryFunctionException( 125 "Telemetry function " + functionName + " does not exist."); 126 } 127 128 //check input types 129 for (int i = 0; i < parameters.length; i++) { 130 Object object = parameters[i]; 131 if (! (object instanceof String || object instanceof Number 132 || object instanceof TelemetryStreamCollection)) { 133 throw new TelemetryFunctionException("Telemetry function parameter should be of type " 134 + "either 'String', 'Number', and/or 'TelemetryStreamCollection'."); 135 } 136 } 137 //delegate computation 138 Object result = functionInfo.getFunction().compute(parameters); 139 //check output types 140 if (! (result instanceof Number || result instanceof TelemetryStreamCollection)) { 141 throw new TelemetryFunctionException("Telemetry function " + functionName 142 + " implementation error. It returns a result of unexpected type."); 143 } 144 return result; 145 } 146 }