/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.core.service; import com.espertech.esper.client.EPException; import com.espertech.esper.client.EPStatement; import com.espertech.esper.client.EventBean; import com.espertech.esper.collection.UniformPair; import com.espertech.esper.epl.core.EngineImportService; import com.espertech.esper.event.NaturalEventBean; import com.espertech.esper.util.JavaClassHelper; import net.sf.cglib.reflect.FastClass; import net.sf.cglib.reflect.FastMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; /** * A result delivery strategy that uses a matching "update" method and * optional start, end, and updateRStream methods, to deliver column-wise to parameters * of the update method. */ public class ResultDeliveryStrategyImpl implements ResultDeliveryStrategy { private static Logger log = LoggerFactory.getLogger(ResultDeliveryStrategyImpl.class); private final EPStatement statement; private final Object subscriber; private final FastMethod updateMethodFast; private final FastMethod startMethodFast; private final boolean startMethodHasEPStatement; private final FastMethod endMethodFast; private final boolean endMethodHasEPStatement; private final FastMethod updateRStreamMethodFast; private final DeliveryConvertor deliveryConvertor; /** * Ctor. * * @param subscriber is the subscriber receiving method invocations * @param deliveryConvertor for converting individual rows * @param method to deliver the insert stream to * @param startMethod to call to indicate when delivery starts, or null if no such indication is required * @param endMethod to call to indicate when delivery ends, or null if no such indication is required * @param rStreamMethod to deliver the remove stream to, or null if no such indication is required * @param statement statement * @param engineImportService engine imports */ public ResultDeliveryStrategyImpl(EPStatement statement, Object subscriber, DeliveryConvertor deliveryConvertor, Method method, Method startMethod, Method endMethod, Method rStreamMethod, EngineImportService engineImportService) { this.statement = statement; this.subscriber = subscriber; this.deliveryConvertor = deliveryConvertor; FastClass fastClass = FastClass.create(engineImportService.getFastClassClassLoader(subscriber.getClass()), subscriber.getClass()); this.updateMethodFast = fastClass.getMethod(method); if (startMethod != null) { this.startMethodFast = fastClass.getMethod(startMethod); this.startMethodHasEPStatement = isMethodAcceptsStatement(startMethod); } else { this.startMethodFast = null; this.startMethodHasEPStatement = false; } if (endMethod != null) { this.endMethodFast = fastClass.getMethod(endMethod); this.endMethodHasEPStatement = isMethodAcceptsStatement(endMethod); } else { this.endMethodFast = null; this.endMethodHasEPStatement = false; } if (rStreamMethod != null) { updateRStreamMethodFast = fastClass.getMethod(rStreamMethod); } else { updateRStreamMethodFast = null; } } public void execute(UniformPair<EventBean[]> result) { if (startMethodFast != null) { int countNew = 0; int countOld = 0; if (result != null) { countNew = count(result.getFirst()); countOld = count(result.getSecond()); } Object[] parameters; if (!startMethodHasEPStatement) { parameters = new Object[]{countNew, countOld}; } else { parameters = new Object[]{statement, countNew, countOld}; } try { startMethodFast.invoke(subscriber, parameters); } catch (InvocationTargetException e) { handle(statement.getName(), log, e, parameters, subscriber, startMethodFast); } catch (Throwable t) { handleThrowable(log, t, null, subscriber, startMethodFast); } } EventBean[] newData = null; EventBean[] oldData = null; if (result != null) { newData = result.getFirst(); oldData = result.getSecond(); } if ((newData != null) && (newData.length > 0)) { for (int i = 0; i < newData.length; i++) { EventBean theEvent = newData[i]; if (theEvent instanceof NaturalEventBean) { NaturalEventBean natural = (NaturalEventBean) theEvent; Object[] parameters = deliveryConvertor.convertRow(natural.getNatural()); try { updateMethodFast.invoke(subscriber, parameters); } catch (InvocationTargetException e) { handle(statement.getName(), log, e, parameters, subscriber, updateMethodFast); } catch (Throwable t) { handleThrowable(log, t, parameters, subscriber, updateMethodFast); } } } } if ((updateRStreamMethodFast != null) && (oldData != null) && (oldData.length > 0)) { for (int i = 0; i < oldData.length; i++) { EventBean theEvent = oldData[i]; if (theEvent instanceof NaturalEventBean) { NaturalEventBean natural = (NaturalEventBean) theEvent; Object[] parameters = deliveryConvertor.convertRow(natural.getNatural()); try { updateRStreamMethodFast.invoke(subscriber, parameters); } catch (InvocationTargetException e) { handle(statement.getName(), log, e, parameters, subscriber, updateRStreamMethodFast); } catch (Throwable t) { handleThrowable(log, t, parameters, subscriber, updateRStreamMethodFast); } } } } if (endMethodFast != null) { Object[] parameters = endMethodHasEPStatement ? new Object[]{statement} : null; try { endMethodFast.invoke(subscriber, parameters); } catch (InvocationTargetException e) { handle(statement.getName(), log, e, null, subscriber, endMethodFast); } catch (Throwable t) { handleThrowable(log, t, null, subscriber, endMethodFast); } } } /** * Handle the exception, displaying a nice message and converting to {@link EPException}. * * @param logger is the logger to use for error logging * @param e is the exception * @param parameters the method parameters * @param subscriber the object to deliver to * @param method the method to call * @param statementName statement name * @throws EPException converted from the passed invocation exception */ protected static void handle(String statementName, Logger logger, InvocationTargetException e, Object[] parameters, Object subscriber, FastMethod method) { String message = JavaClassHelper.getMessageInvocationTarget(statementName, method.getJavaMethod(), subscriber.getClass().getName(), parameters, e); logger.error(message, e.getTargetException()); } /** * Handle the exception, displaying a nice message and converting to {@link EPException}. * * @param logger is the logger to use for error logging * @param t is the throwable * @param parameters the method parameters * @param subscriber the object to deliver to * @param method the method to call * @throws EPException converted from the passed invocation exception */ protected static void handleThrowable(Logger logger, Throwable t, Object[] parameters, Object subscriber, FastMethod method) { String message = "Unexpected exception when invoking method '" + method.getName() + "' on subscriber class '" + subscriber.getClass().getSimpleName() + "' for parameters " + ((parameters == null) ? "null" : Arrays.toString(parameters)) + " : " + t.getClass().getSimpleName() + " : " + t.getMessage(); logger.error(message, t); } private int count(EventBean[] events) { if (events == null) { return 0; } int count = 0; for (int i = 0; i < events.length; i++) { EventBean theEvent = events[i]; if (theEvent instanceof NaturalEventBean) { count++; } } return count; } private static boolean isMethodAcceptsStatement(Method method) { return method.getParameterTypes().length > 0 && method.getParameterTypes()[0] == EPStatement.class; } }