[
https://issues.apache.org/jira/browse/METRON-690?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15876914#comment-15876914
]
ASF GitHub Bot commented on METRON-690:
---------------------------------------
Github user nickwallen commented on a diff in the pull request:
https://github.com/apache/incubator-metron/pull/450#discussion_r102322439
--- Diff:
metron-analytics/metron-profiler-client/src/main/java/org/apache/metron/profiler/client/window/WindowProcessor.java
---
@@ -0,0 +1,321 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.metron.profiler.client.window;
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.apache.metron.common.dsl.ErrorListener;
+import org.apache.metron.common.dsl.GrammarUtils;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Token;
+import org.apache.metron.common.utils.ConversionUtils;
+import
org.apache.metron.profiler.client.window.generated.WindowBaseListener;
+import org.apache.metron.profiler.client.window.generated.WindowLexer;
+import org.apache.metron.profiler.client.window.generated.WindowParser;
+import org.apache.metron.profiler.client.window.predicates.DayPredicates;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+
+public class WindowProcessor extends WindowBaseListener {
+ private Throwable throwable;
+ private Deque<Token<?>> stack;
+ private static final Token<Object> LIST_MARKER = new Token<>(null,
Object.class);
+ private static final Token<Object> DAY_SPECIFIER_MARKER = new
Token<>(null, Object.class);
+ private Window window;
+
+ public WindowProcessor() {
+ this.stack = new ArrayDeque<>();
+ this.window = new Window();
+ }
+
+ public Window getWindow() {
+ return window;
+ }
+
+ private void enterList() {
+ stack.push(LIST_MARKER);
+ }
+
+ private List<Function<Long, Predicate<Long>>> getPredicates() {
+ LinkedList<Function<Long, Predicate<Long>>> predicates = new
LinkedList<>();
+ while (true) {
+ Token<?> token = stack.pop();
+ if (token == LIST_MARKER) {
+ break;
+ } else {
+ predicates.addFirst((Function<Long, Predicate<Long>>)
token.getValue());
+ }
+ }
+ return predicates;
+ }
+
+ @Override
+ public void exitIdentifier(WindowParser.IdentifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ stack.push(new Token<>(ctx.getText().substring(1), String.class));
+ }
+
+ @Override
+ public void enterSpecifier(WindowParser.SpecifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ stack.push(DAY_SPECIFIER_MARKER);
+ }
+
+ @Override
+ public void exitSpecifier(WindowParser.SpecifierContext ctx) {
+ LinkedList<String> args = new LinkedList<>();
+
+ while (true) {
+ Token<?> token = stack.pop();
+ if (token == DAY_SPECIFIER_MARKER) {
+ break;
+ } else {
+ args.addFirst((String) token.getValue());
+ }
+ }
+ String specifier = args.removeFirst();
+ List<String> arg = args.size() > 0?args:new ArrayList<>();
+ Function<Long, Predicate<Long>> predicate = null;
+ try {
+ if (specifier.equals("THIS DAY OF THE WEEK") ||
specifier.equals("THIS DAY OF WEEK")) {
+ predicate = now ->
DayPredicates.dayOfWeekPredicate(DayPredicates.getDayOfWeek(now));
+ } else {
+ final Predicate<Long> dayOfWeekPredicate =
DayPredicates.create(specifier, arg);
+ predicate = now -> dayOfWeekPredicate;
+ }
+ stack.push(new Token<>(predicate, Function.class));
+ }
+ catch(Throwable t) {
+ throwable = t;
+ }
+ }
+
+ @Override
+ public void exitDay_specifier(WindowParser.Day_specifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ String specifier = ctx.getText().toUpperCase();
+ if(specifier.length() == 0 && ctx.exception != null){
+ IllegalStateException ise = new IllegalStateException("Invalid day
specifier: " + ctx.getStart().getText(), ctx.exception);
+ throwable = ise;
+ throw ise;
+ }
+ if(specifier.endsWith("S")) {
+ specifier = specifier.substring(0, specifier.length() - 1);
+ }
+ stack.push(new Token<>(specifier, String.class));
+ }
+
+ @Override
+ public void
enterExcluding_specifier(WindowParser.Excluding_specifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ enterList();
+ }
+
+ @Override
+ public void
exitExcluding_specifier(WindowParser.Excluding_specifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ window.setExcludes(getPredicates());
+ }
+
+ @Override
+ public void
enterIncluding_specifier(WindowParser.Including_specifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ enterList();
+ }
+
+ @Override
+ public void
exitIncluding_specifier(WindowParser.Including_specifierContext ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ window.setIncludes(getPredicates());
+ }
+
+ private void setFromTo(int from, int to) {
+ window.setEndMillis(now -> now - Math.min(to, from));
+ window.setStartMillis(now -> now - Math.max(from, to));
+ }
+
+ @Override
+ public void
exitFromToDuration(org.apache.metron.profiler.client.window.generated.WindowParser.FromToDurationContext
ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ Token<?> toInterval = stack.pop();
+ Token<?> fromInterval = stack.pop();
+ Integer to = (Integer)toInterval.getValue();
+ Integer from = (Integer)fromInterval.getValue();
+ setFromTo(from, to);
+ }
+
+ @Override
+ public void
exitFromDuration(org.apache.metron.profiler.client.window.generated.WindowParser.FromDurationContext
ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ Token<?> timeInterval = stack.pop();
+ Integer from = (Integer)timeInterval.getValue();
+ setFromTo(from, 0);
+ }
+
+ @Override
+ public void
exitSkipDistance(org.apache.metron.profiler.client.window.generated.WindowParser.SkipDistanceContext
ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ Token<?> timeInterval = stack.pop();
+ Integer width = (Integer)timeInterval.getValue();
+ window.setSkipDistance(width);
+ }
+
+ @Override
+ public void
exitWindowWidth(org.apache.metron.profiler.client.window.generated.WindowParser.WindowWidthContext
ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ Token<?> timeInterval = stack.pop();
+ Integer width = (Integer)timeInterval.getValue();
+ window.setBinWidth(width);
+ window.setStartMillis(now -> now - width);
+ window.setEndMillis(now -> now);
+ }
+
+ @Override
+ public void
exitTimeInterval(org.apache.metron.profiler.client.window.generated.WindowParser.TimeIntervalContext
ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ Token<?> timeUnit = stack.pop();
+ Token<?> timeDuration = stack.pop();
+ int duration = ConversionUtils.convert(timeDuration.getValue(),
Integer.class);
+ TimeUnit unit = (TimeUnit) timeUnit.getValue();
+ stack.push(new Token<>((int)unit.toMillis(duration), Integer.class));
+ }
+
+ @Override
+ public void
exitTimeAmount(org.apache.metron.profiler.client.window.generated.WindowParser.TimeAmountContext
ctx) {
+ if(checkForException(ctx)) {
+ return;
+ }
+ if(ctx.getText().length() == 0) {
+ throwable = new IllegalStateException("Unable to parse empty
string.");
+ return;
+ }
+ int duration = Integer.parseInt(ctx.getText());
+ stack.push(new Token<>(duration, Integer.class));
+ }
+
+ @Override
+ public void
exitTimeUnit(org.apache.metron.profiler.client.window.generated.WindowParser.TimeUnitContext
ctx) {
+ checkForException(ctx);
+ switch(normalizeTimeUnit(ctx.getText())) {
+ case "DAY":
+ stack.push(new Token<>(TimeUnit.DAYS, TimeUnit.class));
+ break;
+ case "HOUR":
+ stack.push(new Token<>(TimeUnit.HOURS, TimeUnit.class));
+ break;
+ case "MINUTE":
+ stack.push(new Token<>(TimeUnit.MINUTES, TimeUnit.class));
+ break;
+ case "SECOND":
+ stack.push(new Token<>(TimeUnit.SECONDS, TimeUnit.class));
+ break;
+ default:
+ throw new IllegalStateException("Unsupported time unit: " +
ctx.getText()
+ + ". Supported units are limited to: day, hour, minute,
second "
+ + "with any pluralization or capitalization.");
+ }
+ }
+
+ private boolean checkForException(ParserRuleContext ctx) {
+ if(throwable != null) {
+ return true;
+ }
+ else if(ctx.exception != null) {
+ return true;
+ }
+ return false;
+ }
+
+ private static String normalizeTimeUnit(String s) {
+ String ret = s.toUpperCase().replaceAll("[^A-Z]", "");
+ if(ret.endsWith("S")) {
+ return ret.substring(0, ret.length() - 1);
+ }
+ return ret;
+ }
+
+ public static Window parse(String statement) throws ParseException {
--- End diff --
Why do you call this `parse`? If I am reading the code correctly it seems
like this tokenizes, parses, executes the expression and returns a result.
> Create a DSL-based timestamp lookup for profiler to enable sparse windows
> -------------------------------------------------------------------------
>
> Key: METRON-690
> URL: https://issues.apache.org/jira/browse/METRON-690
> Project: Metron
> Issue Type: New Feature
> Reporter: Casey Stella
>
> I propose that we support the following features:
> * A starting point that is not current time
> * Sparse bins (i.e. the last hour for every tuesday for the last month)
> * The ability to skip events (e.g. weekends, holidays)
> This would result in a new function with the following arguments:
> from - The lookback starting point (default to now)
> fromUnits - The units for the lookback starting point
> to - The ending point for the lookback window (default to from + binSize)
> toUnits - The units for the lookback ending point
> including - A list of conditions which we would skip.
> weekend
> holiday
> sunday through saturday
> excluding - A list of conditions which we would skip.
> weekend
> holiday
> sunday through saturday
> binSize - The size of the lookback bin
> binUnits - The units of the lookback bin
> Given the number of arguments and their complexity and the fact that many,
> many are optional,
> PROFILE_LOOKBACK accept a string backed by a DSL to express these criteria
> Base Case: A lookback of 1 hour ago
> PROFILE_LOOKBACK( '1 hour bins from now')
> Example 1: The same time window every tuesday for the last month starting one
> hour ago
> Just to make this as clear as possible, if this is run at 3PM on Monday
> January 23rd, 2017, it would include the following bins:
> January 17th, 2PM - 3PM
> January 10th, 2PM - 3PM
> January 3rd, 2PM - 3PM
> December 27th, 2PM - 3PM
> PROFILE_LOOKBACK( '1 hour bins from 1 hour to 1 month including tuesdays')
> Example 2: The same time window every sunday for the last month starting one
> hour ago skipping holidays
> Just to make this as clear as possible, if this is run at 3PM on Monday
> January 22rd, 2017, it would include the following bins:
> January 16th, 2PM - 3PM
> January 9th, 2PM - 3PM
> January 2rd, 2PM - 3PM
> NOT December 25th
> PROFILE_LOOKBACK( '1 hour bins from 1 hour to 1 month including tuesdays
> excluding holidays')
--
This message was sent by Atlassian JIRA
(v6.3.15#6346)