Java tutorial
/* * 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"); } } } }