/** * Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * icense version 2 and the aforementioned licenses. * * This program 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 General * Public License for more details. */ /** * Part of the diploma thesis of Thomas Everding. * @author Thomas Everding */ package org.n52.ses.eml.v002.filterlogic.esper; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Vector; import net.opengis.eml.x002.EventAttributeType; import net.opengis.eml.x002.SimplePatternType; import net.opengis.eml.x002.EMLDocument.EML; import net.opengis.eml.x002.SimplePatternType.PropertyRestrictions; import net.opengis.fes.x20.FilterType; import org.apache.muse.util.xml.XmlUtils; import org.apache.xmlbeans.XmlObject; import org.n52.ses.api.IUnitConverter; import org.n52.ses.api.eml.IEML; import org.n52.ses.api.eml.ILogicController; import org.n52.ses.api.eml.IPatternSimple; import org.n52.ses.api.event.DataTypesMap; import org.n52.ses.api.event.MapEvent; import org.n52.ses.api.ws.ISubscriptionManager; import org.n52.ses.eml.v002.filterlogic.esper.StatementListener; import org.n52.ses.eml.v002.Constants; import org.n52.ses.eml.v002.filterlogic.EMLParser; import org.n52.ses.eml.v002.pattern.APattern; import org.n52.ses.eml.v002.pattern.PatternComplex; import org.n52.ses.eml.v002.pattern.PatternRepetitive; import org.n52.ses.eml.v002.pattern.PatternSimple; import org.n52.ses.eml.v002.pattern.PropRestriction; import org.n52.ses.eml.v002.pattern.SelFunction; import org.n52.ses.eml.v002.pattern.Statement; import org.n52.ses.util.common.FaaSaaPilotConstants; import org.n52.ses.util.xml.EMLHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import com.espertech.esper.client.Configuration; import com.espertech.esper.client.EPServiceProvider; import com.espertech.esper.client.EPServiceProviderManager; import com.espertech.esper.client.EPStatement; import com.espertech.esper.client.EPStatementException; import com.vividsolutions.jts.geom.Geometry; /** * central class for executing a set of esper EPL statements for a single process * * @author Thomas Everding * */ public class EsperController implements ILogicController { private static final String CUSTOM_ESPER_FUNCTIONS_NAMESPACE= "org.n52.ses.eml.v002.filterlogic.esper.customFunctions.*"; /* * Logger instance for this class */ private static final Logger logger = LoggerFactory .getLogger(EsperController.class); private Configuration config; private EPServiceProvider epService; private HashMap<String, StatementListener> listeners; private HashMap<String, CountingListener> countingListeners; private HashMap<String, EPStatement> epStatements; private EMLParser parser; private ISubscriptionManager subMgr; // private OutputDescription[] outputDescriptions; private HashMap<String, Object> inputEventDataTypes; // private EMLProcess process; // private InputDescription[] inputDescriptions; private HashMap<String, Object> eventProperties; /** * * Constructor * */ public EsperController() { this.config = new Configuration(); this.listeners = new HashMap<String, StatementListener>(); this.countingListeners = new HashMap<String, CountingListener>(); // this.timerListeners = new HashMap<String, TimerListener>(); this.epStatements = new HashMap<String, EPStatement>(); this.inputEventDataTypes = new HashMap<String, Object>(); } /** * * Constructor * * @param sub subscription manager */ public EsperController(ISubscriptionManager sub) { this(); this.subMgr = sub; } /** * Registers an event with a name at the engine. Nestable maps are allowed. * * @param eventName Name of the event used in the patterns (mostly the inputName). * @param properties Properties of the event. Each event is send as an HashMap. These Properties must * contain an entry for each key in the HashMap containing the data type of the HashMap value. */ @Override public synchronized void registerEvent(String eventName, Map<String, Object> properties) { // logger.info("registering an event with following properties:"); // logger.info("\t event name: " + eventName); // // for (String key : properties.keySet()) { // logger.info("\t'" + key + "' of type '" + properties.get(key) + "'"); // } if (!this.config.getEventTypesNestableMapEvents().containsKey(eventName)) { EsperController.logger.info("#### registering event for input " + eventName); this.config.addEventType(eventName, properties); } } /** * * @return the event properties map */ public HashMap<String, Object> getEventProperties() { return this.eventProperties; } // /** // * Registers an event for the given {@link InputDescription} // * // * @param descr description of an process input // */ // private void registerEvent(InputDescription descr) { // String eventName = descr.getName(); // HashMap<String, Object> props = new HashMap<String, Object>(); // // if (descr.getDataType() != SupportedDataTypes.EVENT) { // /* // * check data type // */ // // if (descr.getDataType().equals(SupportedDataTypes.BOOLEAN)) { // //boolean // props.put(MapEvent.VALUE_KEY, Boolean.class); // } // // else if (descr.getDataType().equals(SupportedDataTypes.CATEGORY)) { // //category // props.put(MapEvent.VALUE_KEY, String.class); // } // // else if (descr.getDataType().equals(SupportedDataTypes.COUNT)) { // //count // props.put(MapEvent.VALUE_KEY, Integer.class); // } // // else if (descr.getDataType().equals(SupportedDataTypes.QUANTITY)) { // //quantity // props.put(MapEvent.VALUE_KEY, Double.class); // } // // else if (descr.getDataType().equals(SupportedDataTypes.TEXT)) { // //text // props.put(MapEvent.VALUE_KEY, String.class); // } // // else if (descr.getDataType().equals(SupportedDataTypes.TIME)) { // //time // props.put(MapEvent.VALUE_KEY, Long.class); // } // else { // //unknown // props.put(MapEvent.VALUE_KEY, Object.class); // } // // //save input event data type //// this.inputEventDataTypes.put(eventName, props.get(MapEvent.VALUE_KEY)); // } // else { // /* // * register swe:Event // */ // this.buildEventPropertyDataTypeMap(props, descr); // // //save input event data type //// this.inputEventDataTypes.put(eventName, props); // } // // //register at esper engine // this.registerEvent(eventName, props); // } // /** // * builds the properties map for events (swe:Event) // * // * @param props data type map of the input // * @param description description of the connection delivering an swe:Event // */ // private void buildEventPropertyDataTypeMap(HashMap<String, Object> props, InputDescription description) { // //parse swe:Event data types from description // HashMap<String, Object> dataTypes = EventDataTypeParser.parse(description); // // //add data types to properties // for (String key : dataTypes.keySet()) { // props.put(key, dataTypes.get(key)); // } // } /** * Initializes the controller with the EML patterns. * * @param eml The event patterns in EML. * @throws Exception exceptions thrown during the listener initialization */ @Override public void initialize(IEML eml, IUnitConverter conv) throws Exception{ replacePhenomenonStringsAndConvertUnits(eml, conv); EsperController.logger.debug("initializing esper controller"); this.parser = new EMLParser(this); this.parser.parseEML((EML) eml.getEMLInstance()); HashMap<String, APattern> patterns = this.parser.getPatterns(); /* * register standard property names */ this.registerStandardPropertyNames(); /* * Instantiate propertyNames for esper config */ Map<String, APattern> simplePatterns = new HashMap<String, APattern>(); for (String key : patterns.keySet()) { APattern value = patterns.get(key); if (value instanceof PatternSimple) { simplePatterns.put(key, value); } } //register Map as event type with registered phenomenons/types this.eventProperties = new HashMap<String, Object>(); this.eventProperties.put(MapEvent.START_KEY, Long.class); this.eventProperties.put(MapEvent.END_KEY, Long.class); this.eventProperties.put(MapEvent.STRING_VALUE_KEY, String.class); this.eventProperties.put(MapEvent.DOUBLE_VALUE_KEY, Double.class); this.eventProperties.put(MapEvent.CAUSALITY_KEY, Vector.class); this.eventProperties.put(MapEvent.GEOMETRY_KEY, Geometry.class); this.eventProperties.put(MapEvent.SENSORID_KEY, String.class); this.eventProperties.put(MapEvent.THIS_KEY, Map.class); this.registerNestedFaaSaaTypes(); /* * Get data types for phenomenons. */ /* * we need to register output values of all patterns * at the DataTypesMap */ for (APattern patt : patterns.values()) { for (SelFunction func : patt.getSelectFunctions()) { if (!func.getNewEventName().equals("")) { func.registerOutputProperties(); } } } //TODO: check if string as a value does work (seems not...) DataTypesMap dtm = DataTypesMap.getInstance(); /* * the following loop is needed if a simple pattern * accesses an event property which is not a standard * property known by the EML parser. If so this property * has to be added to the event that is registered with * a data type. */ for (String key : simplePatterns.keySet()) { APattern val = simplePatterns.get(key); for (Object key2 : val.getPropertyNames()) { this.eventProperties.put(key2.toString(), dtm.getDataType(key2.toString())); } } getPropertiesFromPatterns(this.eventProperties, patterns); for (String key : simplePatterns.keySet()) { PatternSimple val = (PatternSimple) simplePatterns.get(key); // logger.info("datatype of field '" + MapEvent.SENSORID_KEY + "' is '" + eventProperties.get(MapEvent.SENSORID_KEY) + "'"); registerEvent(val.getInputName(), this.eventProperties); //add to list of inputs this.inputEventDataTypes.put(val.getInputName(), this.eventProperties); } /* * end of (instantiate propertyNames for esper config) */ //register custom guards this.registerCustomGuards(); //register custom functions this.registerCustomFunctions(); //register custom views this.registerCustomViews(); //build listeners this.buildListeners(patterns); //log the statements this.logStatements(); //initialize esper this.epService = EPServiceProviderManager.getProvider("ses:id:"+ this.hashCode(), this.config); //initialize repetitive count listeners this.initializeCountingListeners(); //initialize listeners this.initializeListeners(); // //register debug listeners // this.buildDebugListeners(); // //initialize timer listeners // this.initializeTimerListeners(); // // //publish one internal timer event for every timer pattern (start these patterns) // this.startTimerPatterns(); //send initial counting event this.sendInitialCountingEvent(); if (logger.isDebugEnabled()) logger.debug("esper controller is ready"); } /** * registers the nested types for the FAA SAA dissemination pilot */ private void registerNestedFaaSaaTypes() { Map<String, Object> airspLayerMap = new HashMap<String, Object>(); airspLayerMap.put(FaaSaaPilotConstants.UPPER_LIMIT_NAME, Double.class); airspLayerMap.put(FaaSaaPilotConstants.LOWER_LIMIT_NAME, Double.class); Map<String, Object> levelsMap = new HashMap<String, Object>(); levelsMap.put(FaaSaaPilotConstants.AIRSPACE_LAYER_NAME, airspLayerMap); Map<String, Object> airspActExtMap = new HashMap<String, Object>(); airspActExtMap.put(FaaSaaPilotConstants.RESERVATION_PHASE_NAME, String.class); Map<String, Object> extensionMap = new HashMap<String, Object>(); extensionMap.put(FaaSaaPilotConstants.AIRSPACE_ACTIVATION_EXTENSION_NAME, airspActExtMap); Map<String, Object> airspActivMap = new HashMap<String, Object>(); airspActivMap.put(FaaSaaPilotConstants.EXTENSION_NAME, extensionMap); airspActivMap.put(FaaSaaPilotConstants.LEVELS_NAME, levelsMap); airspActivMap.put(FaaSaaPilotConstants.STATUS_NAME, String.class); Map<String, Object> activationMap = new HashMap<String, Object>(); activationMap.put(FaaSaaPilotConstants.AIRSPACE_ACTIVATION_NAME, airspActivMap); Map<String, Object> validTimeMap = new HashMap<String, Object>(); validTimeMap.put(FaaSaaPilotConstants.TIME_PERIOD_NAME, String.class); Map<String, Object> airspTSMap = new HashMap<String, Object>(); airspTSMap.put(FaaSaaPilotConstants.ACTIVATION_NAME, activationMap); airspTSMap.put(FaaSaaPilotConstants.VALID_TIME_NAME, validTimeMap); Map<String, Object> tsMap = new HashMap<String, Object>(); tsMap.put(FaaSaaPilotConstants.AIRSPACE_TS_NAME, airspTSMap); Map<String, Object> identifierMap = new HashMap<String, Object>(); identifierMap.put(FaaSaaPilotConstants.VALUE_NAME, String.class); identifierMap.put(FaaSaaPilotConstants.CODE_SPACE_NAME, String.class); Map<String, Object> airspMap = new HashMap<String, Object>(); airspMap.put(FaaSaaPilotConstants.IDENTIFIER_NAME, identifierMap); airspMap.put(FaaSaaPilotConstants.TIMESLICE_NAME, tsMap); this.eventProperties.put(FaaSaaPilotConstants.AIRSPACE_NAME, airspMap); } /** * Registers the standard property names at the * data types map. */ private void registerStandardPropertyNames() { //get data types map DataTypesMap dtm = DataTypesMap.getInstance(); //register types dtm.registerNewDataType(MapEvent.SENSORID_KEY, String.class); dtm.registerNewDataType(MapEvent.STRING_VALUE_KEY, String.class); dtm.registerNewDataType(MapEvent.DOUBLE_VALUE_KEY, Double.class); dtm.registerNewDataType(MapEvent.FOI_ID_KEY, String.class); dtm.registerNewDataType(MapEvent.START_KEY, Long.class); dtm.registerNewDataType(MapEvent.END_KEY, Long.class); dtm.registerNewDataType(MapEvent.OBSERVED_PROPERTY_KEY, String.class); //FAA SAA Pilot not nested dtm.registerNewDataType(MapEvent.RESERVATION_PHASE_KEY, String.class); dtm.registerNewDataType(MapEvent.STAUS_KEY, String.class); dtm.registerNewDataType(MapEvent.IDENTIFIER_VALUE_KEY, String.class); dtm.registerNewDataType(MapEvent.IDENTIFIER_CODESPACE_KEY, String.class); dtm.registerNewDataType(MapEvent.VALID_TIME_KEY, String.class); dtm.registerNewDataType(MapEvent.LOWER_LIMIT_KEY, Double.class); dtm.registerNewDataType(MapEvent.UPPER_LIMIT_KEY, Double.class); //FAA SAA Pilot nested properties dtm.registerNewDataType(FaaSaaPilotConstants.AIRSPACE_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.IDENTIFIER_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.BOUNDED_BY_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.TIMESLICE_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.AIRSPACE_TS_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.VALID_TIME_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.ACTIVATION_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.AIRSPACE_ACTIVATION_EXTENSION_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.LEVELS_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.AIRSPACE_LAYER_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.EXTENSION_NAME, Map.class); dtm.registerNewDataType(FaaSaaPilotConstants.AIRSPACE_ACTIVATION_EXTENSION_NAME, Map.class); } private void getPropertiesFromPatterns(Map<String, Object> properties, Map<String, APattern> patternMap) { APattern pat; String curr; DataTypesMap dtm = DataTypesMap.getInstance(); for (String key : patternMap.keySet()) { pat = patternMap.get(key); for (Object s : pat.getPropertyNames()) { curr = s.toString(); if (curr.contains("/")) { curr = curr.substring(curr.indexOf("/")+1, curr.length()); } else { curr = curr.substring(curr.indexOf(".")+1, curr.length()); } properties.put(curr, dtm.getDataType(curr)); } } } /** * registers the namespace of the custom functions */ private void registerCustomFunctions() { //standard namespace this.config.addImport(CUSTOM_ESPER_FUNCTIONS_NAMESPACE); } /** * registers custom implemented ViewSupports */ private void registerCustomViews() { //the dynamic spatial buffer view this.config.addPlugInView("custom", "dynamicSpatialBuffer", "org.n52.ses.eml.v002.views.DynamicSpatialBufferViewFactory"); } /** * logs all created statements */ private void logStatements() { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Statements:"); for (String key : this.listeners.keySet()) { sb.append("\n\t" + key); } for (String key : this.countingListeners.keySet()) { sb.append("\n\t" + key); } sb.append("\n--"); EsperController.logger.debug(sb.toString()); } } /** * registers the custom guards */ private void registerCustomGuards() { // //greater than // this.config.addPlugInPatternGuard(Constants.GUARD_COMPARISON_NAMESPACE, // "greater", // "de.ifgi.lehre.thesisEverding.eml.complexGuard.comparison.GreaterThanGuardFactory"); } /** * registers debug listeners for every statement */ @SuppressWarnings("unused") private void buildDebugListeners() { //a debug listener for every statement EPStatement statement; for (String key : this.epStatements.keySet()) { statement = this.epStatements.get(key); statement.addListener(new DebugListener()); } } /** * sends an initial counting event to all counting listeners */ private void sendInitialCountingEvent() { CountingListener cListener; for (String key : this.countingListeners.keySet()) { cListener = this.countingListeners.get(key); //publish start event for counting Date now = new Date(); MapEvent event = new MapEvent(now.getTime(), now.getTime()); event.put(MapEvent.VALUE_KEY, 1); this.sendEvent(cListener.getInputEventName(), event); } } /** * Sends an event to the esper runtime. * * @param name The name of the event. * @param event The event itself. */ @Override public synchronized void sendEvent(String name, MapEvent event) { sendEvent(name, event, true); } public synchronized void sendEvent(String name, MapEvent event, boolean persist) { // this.logger.info("event is " + ((event != null) ? "not " : "") + "null!"); StringBuilder sb = new StringBuilder(); // sb.append("\n"); sb.append("posting new event (" + new Date().getTime() + "):"); sb.append("\n\tname: " + name); // sb.append("\n" + event.toString()); // sb.append("\n\tevent: " + event.getClass().getName()); // sb.append("current time: " + new Date().getTime()); // for (String key : event.keySet()) { // sb.append("\n\t" + key + ": \t" + event.get(key)); // } // sb.append("\n"); EsperController.logger.debug(sb.toString()); this.epService.getEPRuntime().sendEvent(event, name); if (persist && this.subMgr.isStreamPersistenceEnabled()) { this.subMgr.persistEvent(event, name); } } /** * builds and registers the listeners for all patterns * * @param patterns the patterns */ private void buildListeners(HashMap<String, APattern> patterns) { HashMap<String, Object> completedPatterns = new HashMap<String, Object>(); Vector<APattern> uncompletedPatterns = new Vector<APattern>(); APattern patt; //check every pattern for (String key : patterns.keySet()) { patt = patterns.get(key); //first run: only simple and timer patterns if (patt instanceof PatternComplex || patt instanceof PatternRepetitive) { //these patterns need other patterns to be registered first uncompletedPatterns.add(patt); continue; } this.buildListenersForPattern(patt); completedPatterns.put(patt.getPatternID(), patt); } //second run: complex and repetitive patterns int i = -1; int maxTests = uncompletedPatterns.size() * 3; while (uncompletedPatterns.size() > 0) { uncompletedPatterns = this.buildComplexListeners(completedPatterns, uncompletedPatterns); //check for loop i++; if (i > maxTests) { EsperController.logger.warn("One of the patterns can not be build or there is a loop in the patterns. This is not allowed."); break; } } } /** * builds the listeners for patterns that use other patterns (complex and repetitive) * * @param completedPatterns already completed patterns * @param uncompletedPatterns patterns without listener * @return the patterns witch could not be build */ private Vector<APattern> buildComplexListeners(HashMap<String, Object> completedPatterns, Vector<APattern> uncompletedPatterns) { Vector<APattern> stillUncomPatterns = new Vector<APattern>(); PatternComplex cp; PatternRepetitive rp; for (APattern pat : uncompletedPatterns) { //check if all internal patterns are already completed if (pat instanceof PatternComplex) { //complex pattern cp = (PatternComplex) pat; if (completedPatterns.containsKey(cp.getFirstPatternID()) && completedPatterns.containsKey(cp.getSecondPatternID())) { //build pattern listeners this.buildListenersForPattern(pat); completedPatterns.put(pat.getPatternID(), pat); } else { //append to list, try later stillUncomPatterns.add(pat); } } else { //repetitive pattern rp = (PatternRepetitive) pat; if (completedPatterns.containsKey(rp.getPatternToRepeatID())) { //build pattern listeners this.buildListenersForPattern(pat); completedPatterns.put(pat.getPatternID(), pat); } else { //append to list, try later stillUncomPatterns.add(pat); } } } return stillUncomPatterns; } /** * builds and registers the listeners for a single pattern * * @param pattern the pattern */ private void buildListenersForPattern(APattern pattern) { EsperController.logger.debug("building listener for pattern " + pattern.getPatternID()); if (pattern instanceof PatternRepetitive) { /* * repetitive pattern needs two statements per select function * * first statement is the counting statement, the others are the selecting statements. */ Statement[] statements = pattern.createEsperStatements(); //create new CountingListener for the first statement CountingListener cListener = new CountingListener(this, ((PatternRepetitive) pattern).getInputEventName()); this.countingListeners.put(statements[0].getStatement(), cListener); //create listeners for the selecting statements for (int i = 1; i < statements.length; i++) { this.buildListener(statements[i]); } } else { /* * other pattern only needs one per statement */ for (Statement statement : pattern.createEsperStatements()) { this.buildListener(statement); } } } /** * builds the listener for a single statement * * @param statement the statement */ private void buildListener(Statement statement) { StatementListener listener; //create new listener /* * here we need a workaround. esper is not capable * of sending first/last events of a batch-view - it * instead sends all events in that view. check the EML * if first or last event is selected and then register * LastEventStatementListener or FirstEventStatementListener */ if (statement.getView() != null && statement.getView().getViewName().equals(Constants.VIEW_SELECT_LAST)) { listener = new LastEventStatementListener(statement, this, this.subMgr); } else { listener = new StatementListener(statement, this, this.subMgr); } //add listener to map this.listeners.put(statement.getStatement(), listener); } /** * initializes the listeners (registers them at the statements) * @throws Exception */ private synchronized void initializeListeners() throws Exception { //for every statement in the map EPStatement epStatement; for (String statement : this.listeners.keySet()) { /* * register statements at engine. * Try-Catch needed for better SoapFaults for users -> * a statement can fail if the property was not registered * in the DataTypesMap */ epStatement = null; try { epStatement = this.epService.getEPAdministrator().createEPL(statement); } catch (EPStatementException e) { EsperController.logger.warn(e.getMessage()); StringBuilder sb = new StringBuilder(); for (StackTraceElement ste : e.getStackTrace()) { sb.append("\n" + ste.toString()); } EsperController.logger.warn(sb.toString()); if (e.getMessage().contains("Implicit conversion")) { throw new Exception("Registration of statement failed. Looks like your observerd property was" + " not registered by any publisher.\r\n" + "If you used \"value\" in your Guard, please use \"doubleValue\" or \"stringValue\" instead.\r\n" + "Standard data types:\r\n" + "sensorID = String\r\n" + "stringValue = String\r\n" + "doubleValue = double\r\n" + "startTime = long\r\n" + "endTime = long\r\n" + "observedProperty = String\r\n" + "foiID = String"); } //else throw initial exception throw new Exception("Error in esper statement, possible EML error: '" + e.getMessage() + "'", e); } //register listener at esper statement if (epStatement != null) { epStatement.addListener(this.listeners.get(statement)); //store epStatements this.epStatements.put(statement, epStatement); } // StringBuilder sb = new StringBuilder(); // sb.append("listener registred at engine"); // sb.append("\n\tstatement: " + statement); // sb.append("\n\tlistener number: " + listeners.get(statement).getInstanceNumber()); // sb.append("\n\tlistener state: " + epStatement.getState().toString()); // logger.info(sb.toString()); } } /** * initializes the counting listeners */ private synchronized void initializeCountingListeners() { //for every statement in the map EPStatement epStatement; CountingListener cListener; for (String statement : this.countingListeners.keySet()) { cListener = this.countingListeners.get(statement); //register statement at engine epStatement = this.epService.getEPAdministrator().createEPL(statement); //register listener at esper statement epStatement.addListener(cListener); //store epStatements this.epStatements.put(statement, epStatement); } } /** * Searches for the data type of a property. * * @param fullPropertyName the full EML name of the property * * @return a java.lang.Class or a Map containing Classes and/or further Maps */ @Override public Object getDatatype(String fullPropertyName) { //split into event and property name part String eventName; String propertyName; int lastSlash = fullPropertyName.lastIndexOf("/"); propertyName = fullPropertyName.substring(lastSlash + 1); int lastButOneSlash = fullPropertyName.substring(0, lastSlash).lastIndexOf("/"); if (lastButOneSlash <= 0) { //full name looks like "event/value" eventName = fullPropertyName.substring(0, lastSlash); } else { //full name looks like "event/nestedEvent/value", we need nestedEvent eventName = fullPropertyName.substring(lastButOneSlash + 1, lastSlash); } //check all inputs first // for (InputDescription descr : this.inputDescriptions) { // if (descr.getName().equals(eventName)) { // return DataTypeNameToClassConverter.convert(descr.getDataType()); // } // } //then check property Restrictions for (APattern pat : this.parser.getPatterns().values()) { if (pat instanceof PatternSimple) { PatternSimple pats = (PatternSimple) pat; for (PropRestriction propRes : pats.getPropertyRestrictions()) { if (propRes.getName().equals(propertyName)) { if (propRes.getValue().equals("\"" + MapEvent.DOUBLE_VALUE_KEY + "\"")) { return Number.class; } } } } } //then check all patterns for (APattern pat : this.parser.getPatterns().values()) { for (SelFunction sel : pat.getSelectFunctions()) { if (sel.getNewEventName().equals(eventName)) { //this select function defines the data type return sel.getDataTypes().get(propertyName); } } } return null; } /** * get a map containing all data types of an event * * @param eventName name of the event (only the event name) * * @return a map containing all data types of an event * or the class of the data type if the event is an input event */ @Override public Object getEventDatatype(String eventName) { //check input data types if (this.inputEventDataTypes.containsKey(eventName)) { //event is process input return this.inputEventDataTypes.get(eventName); } //check pattern outputs // logger.info("check pattern outputs (for event name '" + eventName + "'), no. of patterns: " + this.parser.getPatterns().size()); for (APattern pat : this.parser.getPatterns().values()) { // logger.info("no. of select functions: " + pat.getSelectFunctions().size()); for (SelFunction sel : pat.getSelectFunctions()) { // logger.info("new event name: " + sel.getNewEventName()); if (sel.getNewEventName().equals(eventName)) { //event definition found return sel.getDataTypes(); } } } //nothing found EsperController.logger.warn("No data type description found for event '" + eventName + "'."); return null; } // /** // * // * @param name name of the output // * @return the {@link OutputDescription} oft an output or <code>null</code> if there is no output for the // * name // */ // public OutputDescription getOutputDescription(String name) { //// for (OutputDescription descr : this.outputDescriptions) { //// if (descr.getName().equals(name)) { //// //output description found //// return descr; //// } //// } // logger.severe("No description for output '" + name + "' found."); // return null; // } /** * Performs the output of a value. * * @param outputName the name of the output * @param value the value to send */ public void doOutput(String outputName, Object value) { // this.process.doOutput(outputName, value); } // /** // * @return the epService // */ // public EPServiceProvider getEpService() { // return this.epService; // } // /** // * test main // * // * @param args // */ // public static void main(String[] args) { // /* // * Initialize the Logger (Config file is located in folder 'xml' with name 'log4j.xml') // */ // org.apache.log4j.xml.DOMConfigurator.configureAndWatch("xml" + java.io.File.separator + "log4j.xml", // 60 * 1000); // // EsperController c = new EsperController(null); // c.test(); // } /** * Returns the newEventName of a given pattern * * @param patternID id of the pattern * @param selectFunctionNumber number of the select function which results are counted * * @return the newEventName of the pattern */ @SuppressWarnings("all") public String getNewEventName(String patternID, int selectFunctionNumber) { //get all patterns from parser HashMap<String, APattern> patterns = this.parser.getPatterns(); //search for pattern if (!this.parser.getPatterns().containsKey(patternID)) { this.logger.warn("pattern ID (" + patternID + ") not found"); return null; } APattern pattern = patterns.get(patternID); //search for select function if (!(pattern.getSelectFunctions().size() > selectFunctionNumber)) { if (!(pattern.getSelectFunctions().size() >= 0)) { this.logger.warn("No select function and therefore no 'newEventName' defined in pattern '" + patternID + "'. Can not use this pattern in a repetitive pattern."); return null; } this.logger.warn("The pattern with the id '" + patternID + "does not define at least " + selectFunctionNumber + " selectfunctions. Using first select function instead."); //set number to 0 selectFunctionNumber = 0; } //return newEventName return pattern.getSelectFunctions().get(selectFunctionNumber).getNewEventName(); } /** * @return the EML parser */ public EMLParser getParser() { return this.parser; } /** * * @param statement the esper statement * * @return the registered statement object for the statement or null if not present */ public EPStatement getEPStatement(String statement) { if (this.epStatements.containsKey(statement)) { return this.epStatements.get(statement); } return null; } /** * Retunrs the {@link SubscriptionManager} associated with * this {@link EsperController} * * @return the associated {@link SubscriptionManager} */ public ISubscriptionManager getSubMgr() { return this.subMgr; } /** * Removes all statements and listeners from the esper engine, */ public void removeFromEngine() { for (EPStatement epst : this.epStatements.values()) { EsperController.logger.info("Removing statement: \n\t"+ epst.getText()); epst.removeAllListeners(); epst.destroy(); } /* destroy this complete engine - its independent */ this.epService.destroy(); } @Override public Map<String, IPatternSimple> getSimplePatterns() { Map<String, APattern> patternMap = getParser().getPatterns(); Map<String, IPatternSimple> simplePatterns = new HashMap<String, IPatternSimple>(); for (String key : patternMap.keySet()) { APattern value = patternMap.get(key); if (value instanceof IPatternSimple) { simplePatterns.put(key, (IPatternSimple) value); } } return simplePatterns; } @Override public void pauseAllStatements() { // for (StatementListener eps : this.listeners.values()) { // eps.pause(); // } } @Override public void resumeAllStatements() { // for (StatementListener eps : this.listeners.values()) { // eps.resume(); // } } /** * replaces phenomenon Strings containing ":" to "__" and * converts any quantity units to its base units. * @param converter the unit converter * @throws Exception exceptions that occur */ public void replacePhenomenonStringsAndConvertUnits(IEML eml, IUnitConverter converter) throws Exception { if (eml.getEMLInstance() != null) { EML emlXml = (EML) eml.getEMLInstance(); FilterType filter = null; SimplePatternType[] patterns = emlXml.getSimplePatterns().getSimplePatternArray(); for (SimplePatternType spt : patterns) { if (spt.isSetGuard()) { filter = spt.getGuard().getFilter(); EMLHelper.replaceForFilter(filter, converter); } PropertyRestrictions propRes = spt.getPropertyRestrictions(); if (propRes != null) { EventAttributeType[] arr = propRes.getPropertyRestrictionArray(); for (EventAttributeType eat : arr) { XmlObject obj = XmlObject.Factory.newInstance(); Element elem = (Element) eat.getValue().getDomNode(); String tempText = XmlUtils.toString(elem.getFirstChild()).trim(); // tempText = tempText.replaceAll(":", "__").replaceAll("\\.", "_"); //TODO unit conversion not performed. obj.newCursor().setTextValue(tempText); eat.setValue(obj); } } } } } }