/* *************************************************************************************** * 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.pattern.observer; import com.espertech.esper.client.EPException; import com.espertech.esper.client.util.TimePeriod; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.time.ExprTimePeriod; import com.espertech.esper.epl.expression.time.TimeAbacus; import com.espertech.esper.pattern.*; import com.espertech.esper.schedule.ScheduleParameterException; import com.espertech.esper.util.MetaDefItem; import java.io.Serializable; import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.util.*; /** * Factory for ISO8601 repeating interval observers that indicate truth when a time point was reached. */ public class TimerScheduleObserverFactory implements ObserverFactory, MetaDefItem, Serializable { private final static String NAME_OBSERVER = "Timer-schedule observer"; private final static String ISO_NAME = "iso"; private final static String REPETITIONS_NAME = "repetitions"; private final static String DATE_NAME = "date"; private final static String PERIOD_NAME = "period"; private final static String[] NAMED_PARAMETERS = {ISO_NAME, REPETITIONS_NAME, DATE_NAME, PERIOD_NAME}; private static final long serialVersionUID = 6180792299082096772L; /** * Convertor. */ protected transient TimerScheduleSpecCompute scheduleComputer; protected transient MatchedEventConvertor convertor; protected TimerScheduleSpec spec; public void setObserverParameters(List<ExprNode> parameters, MatchedEventConvertor convertor, ExprValidationContext validationContext) throws ObserverParameterException { this.convertor = convertor; // obtains name parameters Map<String, ExprNamedParameterNode> namedExpressions; try { namedExpressions = ExprNodeUtility.getNamedExpressionsHandleDups(parameters); ExprNodeUtility.validateNamed(namedExpressions, NAMED_PARAMETERS); } catch (ExprValidationException e) { throw new ObserverParameterException(e.getMessage(), e); } boolean allConstantResult; ExprNamedParameterNode isoStringExpr = namedExpressions.get(ISO_NAME); if (namedExpressions.size() == 1 && isoStringExpr != null) { try { allConstantResult = ExprNodeUtility.validateNamedExpectType(isoStringExpr, new Class[]{String.class}); } catch (ExprValidationException ex) { throw new ObserverParameterException(ex.getMessage(), ex); } scheduleComputer = new TimerScheduleSpecComputeISOString(isoStringExpr.getChildNodes()[0]); } else if (isoStringExpr != null) { throw new ObserverParameterException("The '" + ISO_NAME + "' parameter is exclusive of other parameters"); } else if (namedExpressions.size() == 0) { throw new ObserverParameterException("No parameters provided"); } else { allConstantResult = true; ExprNamedParameterNode dateNamedNode = namedExpressions.get(DATE_NAME); ExprNamedParameterNode repetitionsNamedNode = namedExpressions.get(REPETITIONS_NAME); ExprNamedParameterNode periodNamedNode = namedExpressions.get(PERIOD_NAME); if (dateNamedNode == null && periodNamedNode == null) { throw new ObserverParameterException("Either the date or period parameter is required"); } try { if (dateNamedNode != null) { allConstantResult = ExprNodeUtility.validateNamedExpectType(dateNamedNode, new Class[]{String.class, Calendar.class, Date.class, Long.class, LocalDateTime.class, ZonedDateTime.class}); } if (repetitionsNamedNode != null) { allConstantResult &= ExprNodeUtility.validateNamedExpectType(repetitionsNamedNode, new Class[]{Integer.class, Long.class}); } if (periodNamedNode != null) { allConstantResult &= ExprNodeUtility.validateNamedExpectType(periodNamedNode, new Class[]{TimePeriod.class}); } } catch (ExprValidationException ex) { throw new ObserverParameterException(ex.getMessage(), ex); } ExprNode dateNode = dateNamedNode == null ? null : dateNamedNode.getChildNodes()[0]; ExprNode repetitionsNode = repetitionsNamedNode == null ? null : repetitionsNamedNode.getChildNodes()[0]; ExprTimePeriod periodNode = periodNamedNode == null ? null : (ExprTimePeriod) periodNamedNode.getChildNodes()[0]; scheduleComputer = new TimerScheduleSpecComputeFromExpr(dateNode, repetitionsNode, periodNode); } if (allConstantResult) { try { spec = scheduleComputer.compute(convertor, new MatchedEventMapImpl(convertor.getMatchedEventMapMeta()), null, validationContext.getEngineImportService().getTimeZone(), validationContext.getEngineImportService().getTimeAbacus()); } catch (ScheduleParameterException ex) { throw new ObserverParameterException(ex.getMessage(), ex); } } } public EventObserver makeObserver(PatternAgentInstanceContext context, MatchedEventMap beginState, ObserverEventEvaluator observerEventEvaluator, EvalStateNodeNumber stateNodeId, Object observerState, boolean isFilterChildNonQuitting) { return new TimerScheduleObserver(computeSpecDynamic(beginState, context), beginState, observerEventEvaluator, isFilterChildNonQuitting); } public boolean isNonRestarting() { return true; } public TimerScheduleSpec computeSpecDynamic(MatchedEventMap beginState, PatternAgentInstanceContext context) { if (spec != null) { return spec; } try { return scheduleComputer.compute(convertor, beginState, context.getAgentInstanceContext(), context.getStatementContext().getEngineImportService().getTimeZone(), context.getStatementContext().getEngineImportService().getTimeAbacus()); } catch (ScheduleParameterException e) { throw new EPException("Error computing iso8601 schedule specification: " + e.getMessage(), e); } } private static interface TimerScheduleSpecCompute { public TimerScheduleSpec compute(MatchedEventConvertor convertor, MatchedEventMap beginState, ExprEvaluatorContext exprEvaluatorContext, TimeZone timeZone, TimeAbacus timeAbacus) throws ScheduleParameterException; } private static class TimerScheduleSpecComputeISOString implements TimerScheduleSpecCompute { private final ExprNode parameter; private TimerScheduleSpecComputeISOString(ExprNode parameter) { this.parameter = parameter; } public TimerScheduleSpec compute(MatchedEventConvertor convertor, MatchedEventMap beginState, ExprEvaluatorContext exprEvaluatorContext, TimeZone timeZone, TimeAbacus timeAbacus) throws ScheduleParameterException { Object param = PatternExpressionUtil.evaluate(NAME_OBSERVER, beginState, parameter, convertor, exprEvaluatorContext); String iso = (String) param; if (iso == null) { throw new ScheduleParameterException("Received null parameter value"); } return TimerScheduleISO8601Parser.parse(iso); } } private static class TimerScheduleSpecComputeFromExpr implements TimerScheduleSpecCompute { private final ExprNode dateNode; private final ExprNode repetitionsNode; private final ExprTimePeriod periodNode; private TimerScheduleSpecComputeFromExpr(ExprNode dateNode, ExprNode repetitionsNode, ExprTimePeriod periodNode) { this.dateNode = dateNode; this.repetitionsNode = repetitionsNode; this.periodNode = periodNode; } public TimerScheduleSpec compute(MatchedEventConvertor convertor, MatchedEventMap beginState, ExprEvaluatorContext exprEvaluatorContext, TimeZone timeZone, TimeAbacus timeAbacus) throws ScheduleParameterException { Calendar optionalDate = null; Long optionalRemainder = null; if (dateNode != null) { Object param = PatternExpressionUtil.evaluate(NAME_OBSERVER, beginState, dateNode, convertor, exprEvaluatorContext); if (param instanceof String) { optionalDate = TimerScheduleISO8601Parser.parseDate((String) param); } else if (param instanceof Number) { long msec = ((Number) param).longValue(); optionalDate = Calendar.getInstance(timeZone); optionalRemainder = timeAbacus.calendarSet(msec, optionalDate); } else if (param instanceof Calendar) { optionalDate = (Calendar) param; } else if (param instanceof Date) { optionalDate = Calendar.getInstance(timeZone); optionalDate.setTimeInMillis(((Date) param).getTime()); } else if (param instanceof LocalDateTime) { LocalDateTime ldt = (LocalDateTime) param; Date d = Date.from(ldt.atZone(timeZone.toZoneId()).toInstant()); optionalDate = Calendar.getInstance(timeZone); optionalDate.setTimeInMillis(d.getTime()); } else if (param instanceof ZonedDateTime) { ZonedDateTime zdt = (ZonedDateTime) param; optionalDate = GregorianCalendar.from(zdt); } else if (param == null) { throw new EPException("Null date-time value returned from " + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(dateNode)); } else { throw new EPException("Unrecognized date-time value " + param.getClass()); } } TimePeriod optionalTimePeriod = null; if (periodNode != null) { Object param = PatternExpressionUtil.evaluateTimePeriod(NAME_OBSERVER, beginState, periodNode, convertor, exprEvaluatorContext); optionalTimePeriod = (TimePeriod) param; } Long optionalRepeatCount = null; if (repetitionsNode != null) { Object param = PatternExpressionUtil.evaluate(NAME_OBSERVER, beginState, repetitionsNode, convertor, exprEvaluatorContext); if (param != null) { optionalRepeatCount = ((Number) param).longValue(); } } if (optionalDate == null && optionalTimePeriod == null) { throw new EPException("Required date or time period are both null for " + NAME_OBSERVER); } return new TimerScheduleSpec(optionalDate, optionalRemainder, optionalRepeatCount, optionalTimePeriod); } } }