org.sciflex.plugins.synapse.esper.mediators.helpers.EPLStatementHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.sciflex.plugins.synapse.esper.mediators.helpers.EPLStatementHelper.java

Source

/*
 * SCI-Flex: Flexible Integration of SOA and CEP
 * Copyright (C) 2008, 2009  http://sci-flex.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package org.sciflex.plugins.synapse.esper.mediators.helpers;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;

import javax.activation.DataHandler;
import javax.xml.namespace.QName;

import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPStatement;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.om.OMText;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.MessageContext;
import org.apache.synapse.config.SynapseConfigUtils;
import org.apache.synapse.registry.Registry;
import org.apache.synapse.registry.RegistryEntry;

import org.sciflex.plugins.synapse.esper.core.workflows.MonitoredQueryWorkflow;
import org.sciflex.plugins.synapse.esper.mediators.SynapseListener;
import org.sciflex.plugins.synapse.esper.mediators.editors.EditableRegistryKeyOwner;
import org.sciflex.plugins.synapse.esper.mediators.editors.EditableEPLStatementOwner;
import org.sciflex.plugins.synapse.esper.mediators.monitors.QueryActivityMonitor;

import org.w3c.dom.Document;

/**
 * Provides operations for storing an EPL Statement and resources required
 * to create an EPL statement. This is a helper class designed to be used by
 * the Class Mediators that are used used to connect Esper to synapse.
 * The EPL Statement helper is an {@link EditableRegistryKeyOwner}.
 */
public class EPLStatementHelper
        implements EditableRegistryKeyOwner, EditableEPLStatementOwner, InvokableEPLStatementHelper {

    // FIXME: As of now, there is no mechnism in place to get rid of queries not in use
    // test this and add appropriate logic to achieve this. - Harsha and Senaka

    /**
     * Log associated with the EPL Statement Helper.
     */
    private static final Log log = LogFactory.getLog(EPLStatementHelper.class);

    /**
     * This is the epl statement associated with the Helper.
     */
    protected String eplStatement = null;

    /**
     * Registry key to fetch the EPL Statement.
     */
    private String registryKey = null;

    /**
     * EPStatement associated with EPL Statement provided.
     * @see com.espertech.esper.client.EPStatement
     */
    protected EPStatement statement = null;

    /**
     * Associated EPServiceProvider instance.
     * @see com.espertech.esper.client.EPServiceProvider
     */
    private EPServiceProvider provider = null;

    /**
     * Associated Synapse Listener.
     * @see org.sciflex.plugins.synapse.esper.mediators.SynapseListener
     */
    private SynapseListener listener = null;

    /**
     * Time at which the fetched EPL Statement expires. Synapse allows
     * each Registry interface to implement caching. expiryTime is used
     * to determine whether the cached instance is now expired.
     */
    private long expiryTime = 0L;

    /**
     * Object used to lock modification of expiryTime
     */
    private Object expiryTimeLock = new Object();

    /**
     * Object used to lock modification of statement
     */
    private Object statementLock = new Object();

    /**
     * Object used to lock registry key during fetch.
     */
    private Object registryFetchLock = new Object();

    /**
     * The monitor of query activities.
     */
    private MonitoredQueryWorkflow queryActivityMonitor = null;

    /**
     * Type of epl statement used in creation of helper.
     */
    public static enum EPLStatementType {
        /**
         * Statement is provided as a String.
         */
        DIRECT,
        /**
         * Registry key to fetch statement is provided.
         */
        INDIRECT
    }

    /**
     * Constructor accepting epl statement and EP Service Provider.
     * @param statement valid EPL statement.
     * @param provider associated EPServiceProvider instance.
     */
    public EPLStatementHelper(String statement, EPServiceProvider provider) {
        this(EPLStatementType.DIRECT, statement, provider, null);
    }

    /**
     * Constructor accepting epl statement (or registry key that can be used to
     * fetch a valid EPL statement) and EP Service Provider.
     * @param input valid EPL statement, or registry key that can be used to
     * fetch a valid EPL statement.
     * @param provider associated EPServiceProvider instance.
     */
    public EPLStatementHelper(EPLStatementType type, String input, EPServiceProvider provider) {
        this(type, input, provider, null);
    }

    /**
     * Constructor accepting epl statement EP Service Provider, and listener.
     * @param statement valid EPL statement.
     * @param provider associated EPServiceProvider instance.
     * @param listener associated Synapse Listener.
     */
    public EPLStatementHelper(String statement, EPServiceProvider provider, SynapseListener listener) {
        this(EPLStatementType.DIRECT, statement, provider, listener);
    }

    /**
     * Constructor accepting epl statement (or registry key that can be used to
     * fetch a valid EPL statement) and EP Service Provider and listener.
     * @param input valid EPL statement, or registry key that can be used to
     * fetch a valid EPL statement.
     * @param provider associated EPServiceProvider instance.
     * @param listener associated Synapse Listener.
     */
    public EPLStatementHelper(EPLStatementType type, String input, EPServiceProvider provider,
            SynapseListener listener) {
        this.provider = provider;
        this.listener = listener;
        queryActivityMonitor = new QueryActivityMonitor(this);
        switch (type) {
        case DIRECT:
            try {
                eplStatement = input;
                if (eplStatement != null) {
                    statement = provider.getEPAdministrator().createEPL(eplStatement);
                    queryActivityMonitor.notify(eplStatement);

                    if (listener != null)
                        addListener(listener);
                    log.info("EPL Statement created");
                } else {
                    log.info("EPL Statement creation failed");
                }
            } catch (Exception e) {
                log.error("An error occured while setting EPL statement " + e.getMessage());
            }
            break;
        case INDIRECT:
            registryKey = input;
        }
    }

    /**
     * Adds listener to EPStatement created.
     * @param listener associated Synapse Listener.
     * @see com.espertech.esper.client.EPStatement
     */
    public synchronized void addListener(SynapseListener listener) {
        try {
            synchronized (statementLock) {
                statement.addListener(listener);
            }
        } catch (Exception e) {
            log.error("An error occured while adding listener " + e.getMessage());
        }
    }

    /**
     * Returns associated query activity monitor.
     *
     * @return associated query activity monitor.
     * @see QueryActivityMonitor
     */
    public MonitoredQueryWorkflow getQueryActivity() {
        return queryActivityMonitor;
    }

    /**
     * Obtains Registry Key for editting.
     *
     * @return Registry Key
     */
    public String getRegistryKey() {
        return registryKey;
    }

    /**
     * Changes Registry Key.
     *
     * @param key Registry Key
     */
    public void setRegistryKey(String key) {
        synchronized (registryFetchLock) {
            registryKey = key;
        }
    }

    /**
     * Obtains EPL query for editting.
     *
     * @return EPL query. 
     */
    public String getEPL() {
        return eplStatement;
    }

    /**
     * Changes {@link EPServiceProvider}, and also sets a new
     * statement, for the use of the new provider. This is required
     * because the provider may change whene the CEP Instance changes
     * and, therefore, it is important to set the query for the new
     * CEP instance.
     *
     * @param provider Associated EPServiceProvider instance.
     */
    public void changeProvider(EPServiceProvider provider) {
        if (eplStatement == null) {
            return;
        }
        synchronized (statementLock) {
            this.provider = provider;
            statement = provider.getEPAdministrator().createEPL(eplStatement);
        }
    }

    /**
     * Changes EPL query.
     *
     * @param query    EPL query.
     * @param registry Registry to use.
     */
    public void setEPL(String query, Registry registry) {
        if (registryKey == null) {
            eplStatement = query;
            synchronized (statementLock) {
                statement = provider.getEPAdministrator().createEPL(eplStatement);
                queryActivityMonitor.notify(eplStatement);
            }
            if (listener != null)
                addListener(listener);
        } else if (registry == null) {
            log.error("Cannot lookup Registry");
        } else {
            // You can't fetch while updating. And if you try to do so
            // you may end up getting an incorrect result. Also, changes
            // to registryKey during the process is not allowed.
            synchronized (registryFetchLock) {
                OMNode eplNode = registry.lookup(registryKey);
                OMNode eplNodeNew = null;
                if (eplNode == null) {
                    log.error("Registry lookup for EPL statement failed");
                } else if (eplNode instanceof OMElement) {
                    ((OMElement) eplNode).getAttribute(new QName("value")).setAttributeValue(eplStatement);
                    eplNodeNew = eplNode;
                } else if (eplNode instanceof OMText) {
                    DataHandler dh = (DataHandler) (((OMText) eplNode).getDataHandler());
                    if (dh == null) {
                        log.error("Error getting EPL statement");
                    } else {
                        DataHandler dhNew = null;
                        try {
                            Object content = dh.getContent();
                            if (content instanceof InputStream) {
                                dhNew = new DataHandler(new ByteArrayInputStream(eplStatement.getBytes("UTF-8")),
                                        dh.getContentType());
                            } else if (content instanceof String)
                                dhNew = new DataHandler(eplStatement, dh.getContentType());
                            else {
                                log.error("Content fetched from Registry is not valid");
                            }
                            if (dhNew != null) {
                                OMFactory omFactory = OMAbstractFactory.getOMFactory();
                                eplNodeNew = omFactory.createOMText(dhNew, false);
                            } else {
                                log.error("Failed Creating Data Handler");
                            }
                        } catch (IOException e) {
                            log.error("An error occured while changing EPL statement " + e.getMessage());
                        }
                    }
                } else
                    log.error("Invalid EPL statement object retrieved");
                if (eplNodeNew == null) {
                    log.error("An error occured while changing EPL statement");
                } else {
                    updateRegistryResource(registryKey, eplNodeNew, registry);
                    synchronized (expiryTimeLock) {
                        expiryTime = 0L;
                    }
                }
            }
        }
        log.info("EPL Statement successfully changed");
    }

    /**
     * Method that can be used to update a resource on the registry.
     * This method is synchronized, and can only be called once at
     * a time, providing necessary concurrency, whilst avoiding
     * unpleasent situations.
     *
     * @param key      Registry Key to use
     * @param value    Value to strore
     * @param registry Registry to use
     */
    private synchronized void updateRegistryResource(String key, OMNode value, Registry registry) {
        // No registryFetchLock here, as it is called by a method that is already locked.
        // Attempting to add the lock here results in deadlock.
        registry.updateResource(key, value);
        log.info("Successfully changed Registry Entry");
    }

    /**
     * Indicates whether an update is required to
     * be done.
     *
     * @return true if required, false otherwise.
     */
    public boolean updateRequired() {
        if (registryKey == null)
            return false;
        else if (expiryTime == 0L)
            return true;
        Date now = new Date();
        return (now.getTime() > expiryTime);
    }

    /**
     * Invokes EPL Statement Helper before mediating the current message.
     * @param mc Message Context of the current message.
     */
    public void invoke(MessageContext mc) {
        if (statement != null && !updateRequired()) {
            queryActivityMonitor.incrementCount();
            return;
        }
        if (registryKey == null)
            log.error("EPL statement has not been set");
        else {
            Registry registry = null;
            OMNode eplNode = null;
            RegistryEntry re = null;
            long cachableDuration = 0;
            // Lock Registry key during the fetch.
            synchronized (registryFetchLock) {
                registry = mc.getConfiguration().getRegistry();
                eplNode = registry.lookup(registryKey);
                try {
                    re = registry.getRegistryEntry(registryKey);
                    cachableDuration = re.getCachableDuration();
                } catch (Throwable e) {
                    // FIXME: ESB 1.7.x and Synapse 1.2+ incompatibility can lead to errors, such as
                    // java.lang.NoSuchMethodError which is caught in this block. Hence the use
                    // of Throwable. This should be fixed once we move to ESB 2.0 series.
                    log.error("An error occured while fetching EPL statement Registry Entry Details "
                            + e.getMessage());
                }
            }
            if (eplNode == null) {
                log.error("Registry lookup for EPL statement failed");
                return;
            }
            if (eplNode instanceof OMElement) {
                eplStatement = ((OMElement) eplNode).getAttributeValue(new QName("value"));
                if (eplStatement == null) {
                    log.error("Error getting EPL statement");
                    return;
                }
                try {
                    synchronized (statementLock) {
                        statement = provider.getEPAdministrator().createEPL(eplStatement);
                        queryActivityMonitor.notify(eplStatement);
                    }
                    if (listener != null)
                        addListener(listener);
                    log.info("EPStatement created");
                } catch (Exception e) {
                    log.error("An error occured while setting EPL statement " + e.getMessage());
                }
            } else if (eplNode instanceof OMText) {
                DataHandler dh = (DataHandler) (((OMText) eplNode).getDataHandler());
                if (dh == null) {
                    log.error("Error getting EPL statement");
                    return;
                }
                try {
                    Object content = dh.getContent();
                    if (content instanceof InputStream) {
                        BufferedReader br = new BufferedReader(new InputStreamReader((InputStream) content));
                        StringBuffer sb = new StringBuffer();
                        String str;
                        while ((str = br.readLine()) != null) {
                            sb.append(" ");
                            sb.append(str);
                        }
                        br.close();
                        eplStatement = sb.toString();
                    } else if (content instanceof String)
                        eplStatement = (String) content;
                    else {
                        log.error("Content fetched from Registry is not valid");
                    }
                    if (eplStatement != null)
                        log.info("Got EPL Statement from Registry");
                    else {
                        log.error("Invalid EPL statement");
                        return;
                    }
                } catch (IOException e) {
                    log.error("An error occured while reading EPL statement " + e.getMessage());
                    return;
                }
                try {
                    synchronized (statementLock) {
                        statement = provider.getEPAdministrator().createEPL(eplStatement);
                        queryActivityMonitor.notify(eplStatement);
                    }
                    if (listener != null)
                        addListener(listener);
                    log.info("EPStatement created");
                } catch (Exception e) {
                    log.error("An error occured while setting EPL statement " + e.getMessage());
                }
            } else {
                log.error("Invalid EPL statement object retrieved");
                return;
            }
            queryActivityMonitor.incrementCount();
            Date now = new Date();
            if (cachableDuration != 0) {
                synchronized (expiryTimeLock) {
                    expiryTime = cachableDuration + now.getTime();
                }
                log.info("Cached EPL statement expires in " + cachableDuration + "ms");
            } else {
                synchronized (expiryTimeLock) {
                    expiryTime = now.getTime();
                }
                log.info("Cached EPL statement expires in 0ms");
            }
        }
    }
}