/** * 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.HashMap; import net.opengis.eml.x002.EMLDocument; import org.apache.xmlbeans.XmlObject; import org.n52.ses.api.event.MapEvent; import org.n52.ses.api.ws.INotificationMessage; import org.n52.ses.api.ws.ISubscriptionManager; import org.n52.ses.api.ws.SESConstraintFilter; import org.n52.ses.api.ws.SESFilterCollection; import org.n52.ses.eml.v002.pattern.SelFunction; import org.n52.ses.eml.v002.pattern.Statement; import org.n52.ses.eml.v002.util.EventModelGenerator; import org.n52.ses.eml.v002.util.ThreadPool; import org.n52.ses.util.common.ConfigurationRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.espertech.esper.client.UpdateListener; import com.espertech.esper.client.EventBean; /** * listener for a single esper statement * * @author Thomas Everding * */ public class StatementListener implements UpdateListener{ private Statement statement; private EsperController controller; private boolean doOutput; private ISubscriptionManager subMgr; private static int instanceCount = 1; private int instanceNumber; private static final Logger logger = LoggerFactory .getLogger(StatementListener.class); /** * * Constructor * * @param statement one {@link Statement}, used to configure this listener * @param controller the esper controller with the esper engine */ public StatementListener(Statement statement, EsperController controller) { this.statement = statement; this.controller = controller; this.initialize(); } /** * * Constructor * * @param statement statement one {@link Statement}, used to configure this listener * @param controller the esper controller with the esper engine * @param sub the subscription manager */ public StatementListener(Statement statement, EsperController controller, ISubscriptionManager sub) { this(statement, controller); this.subMgr = sub; } /** * initializes the listener */ @SuppressWarnings("unchecked") private void initialize() { //set instance number this.instanceNumber = instanceCount; instanceCount++; //check for output if (this.statement.getSelectFunction().getOutputName().equals("")) { //TODO (hack for static EML) fix output for StaticEMLDocument if (this.statement.getSelectFunction().getNewEventName().equals("")) { this.doOutput = true; } else { this.doOutput = false; } } else { this.doOutput = true; } //register new event at esper engine SelFunction sel = this.statement.getSelectFunction(); if (!sel.getNewEventName().equals("")) { String eventName = sel.getNewEventName(); //common attributes // HashMap<String, Object> eventProperties = new HashMap<String, Object>(); // eventProperties.put(MapEvent.START_KEY, Long.class); // eventProperties.put(MapEvent.END_KEY, Long.class); // eventProperties.put(MapEvent.CAUSALITY_KEY, Vector.class); HashMap<String, Object> eventProperties = this.controller.getEventProperties(); //register every event attribute //TODO for string as result value maybe start debugging here if (sel.isSingleValueOutput()) { for (String key : sel.getDataTypes().keySet()) { if (!eventProperties.containsKey(key)) eventProperties.put(key, sel.getDataTypes().get(key)); } } else { //nested properties HashMap<String, Object> nestedMap = new HashMap<String, Object>(); for (String key : sel.getDataTypes().keySet()) { nestedMap.put(key, sel.getDataTypes().get(key)); } if (nestedMap.get(MapEvent.VALUE_KEY) instanceof HashMap) { //get inner map nestedMap = (HashMap<String, Object>) nestedMap.get(MapEvent.VALUE_KEY); } //add nested properties for (String key : nestedMap.keySet()) { if (key.equals(MapEvent.START_KEY) || key.equals(MapEvent.END_KEY) || key.equals(MapEvent.CAUSALITY_KEY)) { //do nothing } else { if (!eventProperties.containsKey(key)) eventProperties.put(key, nestedMap.get(key)); } } } // logger.info("registering event properties as outputs from statement: " + statement.getStatement()); // // for (String key : eventProperties.keySet()) { // logger.info("key '" + key + "' has the type '" + eventProperties.get(key) + "'"); // } this.controller.registerEvent(eventName, eventProperties); } } @Override public void update(EventBean[] newEvents, EventBean[] oldEvents) { /* * new matches for the pattern received * * handle every match */ if (newEvents != null) { for (EventBean newEvent : newEvents) { this.handleMatch(newEvent); } } } /** * handles a single pattern match * * @param newEvent the EventBean representing the match */ protected synchronized void handleMatch(EventBean newEvent) { UpdateHandlerThread handler = new UpdateHandlerThread(this, newEvent); //handle match in its own thread using a ThreadPool ThreadPool tp = ThreadPool.getInstance(); tp.execute(handler); } // /** // * Sends the received result to the controller for output // * // * @param resultEvent the result to send // */ // public synchronized void doOutput(MapEvent resultEvent) { // String outputName = this.statement.getSelectFunction().getOutputName(); // // //load output description // if (this.outDescription == null) { // if (this.getOutDescriptionPerformed) { // //output description not found // return; // } // // //try to find output description // this.outDescription = this.controller.getOutputDescription(outputName); // this.getOutDescriptionPerformed = true; // // if (this.outDescription == null) { // //not found // return; // } // } // // //send output (the whole event or only the value) // if (this.outDescription.getDataType().equals(SupportedDataTypes.EVENT)) { // //send event // this.controller.doOutput(outputName, resultEvent); // } // else { // //send only value // this.controller.doOutput(outputName, resultEvent.get(MapEvent.VALUE_KEY)); // } // } /** * Sends the received result to the controller for output * * @param resultEvent the result to send */ public synchronized void doOutput(MapEvent resultEvent) { StatementListener.logger.debug("performing output for statement: " + this.statement.getStatement()); boolean sent = false; /* * For GENESIS the output has to be the old eml:Event */ ConfigurationRegistry config = ConfigurationRegistry.getInstance(); boolean genesisMode = false; Object gm = config.getPropertyForKey(ConfigurationRegistry.USE_FOR_GENESIS); if (gm != null) { genesisMode = Boolean.parseBoolean(gm.toString()); } //check if it is allowed to use the original message. //check also if it is used for GENESIS if (this.statement.getSelectFunction().allowsOriginalMessageAsResult() && !genesisMode) { //get original message INotificationMessage origMessage = resultEvent.getOriginalMessage(); if (origMessage != null) { try { //get message and forward to SESSubscriptionManager StatementListener.logger.debug("sending original message"); this.subMgr.publish(origMessage); sent = true; } catch (Throwable t) { //any other exception occurred, sent is false -> do nothing } } } /* * If not yet sent (original message sending * failed or not allowed) build an ses:Event * and send it. */ if (!sent){ XmlObject eventDoc = null; /* * for GENESIS we do ses:Event output. * otherwise use OGC event model */ if (!genesisMode) { //generate Event model StatementListener.logger.info("generating OGC Event model output"); EventModelGenerator eventGen = new EventModelGenerator(resultEvent); if (this.subMgr.getFilter() instanceof SESFilterCollection) { SESFilterCollection sesFilter = (SESFilterCollection) this.subMgr.getFilter(); if (sesFilter.getConstraintFilter() != null) { //EML or EPL filter? if (sesFilter.getConstraintFilter() instanceof SESConstraintFilter) { SESConstraintFilter sesConstr = (SESConstraintFilter) sesFilter.getConstraintFilter(); eventDoc = eventGen.generateEventDocument( (EMLDocument) sesConstr.getEml().getEMLDocumentInstance()); } } else { eventDoc = eventGen.generateEventDocument(); } } else { eventDoc = eventGen.generateEventDocument(); } } else { //generate SESEvent eventDoc = this.subMgr.generateSESEvent(resultEvent); } sent = this.subMgr.sendSESNotificationMessge(eventDoc); } if (!sent) { StatementListener.logger.warn("An error occured while sending a NotificationMessage" + " with the SubscriptionManager. It was not sent."); } } /** * @return the statement */ public Statement getStatement() { return this.statement; } /** * @return the controller */ public EsperController getController() { return this.controller; } /** * @return the doOutput */ public boolean isDoOutput() { return this.doOutput; } /** * @return the instanceNumber */ public int getInstanceNumber() { return this.instanceNumber; } }