You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2016/05/10 16:53:56 UTC

[4/5] incubator-metron git commit: METRON-141: The ability to do threat triage closes apache/incubator-metron#108

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentConfig.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentConfig.java
new file mode 100644
index 0000000..562a928
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentConfig.java
@@ -0,0 +1,108 @@
+/**
+ * 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.common.configuration.enrichment;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.metron.common.configuration.enrichment.threatintel.ThreatIntelConfig;
+import org.apache.metron.common.utils.JSONUtils;
+
+import java.io.IOException;
+
+public class SensorEnrichmentConfig {
+
+  private String index;
+  private int batchSize;
+  private EnrichmentConfig enrichment = new EnrichmentConfig();
+  private ThreatIntelConfig threatIntel = new ThreatIntelConfig();
+
+  public EnrichmentConfig getEnrichment() {
+    return enrichment;
+  }
+
+  public void setEnrichment(EnrichmentConfig enrichment) {
+    this.enrichment = enrichment;
+  }
+
+  public ThreatIntelConfig getThreatIntel() {
+    return threatIntel;
+  }
+
+  public void setThreatIntel(ThreatIntelConfig threatIntel) {
+    this.threatIntel = threatIntel;
+  }
+
+  public String getIndex() {
+    return index;
+  }
+
+  public void setIndex(String index) {
+    this.index = index;
+  }
+
+
+  public int getBatchSize() {
+    return batchSize;
+  }
+
+  public void setBatchSize(int batchSize) {
+    this.batchSize = batchSize;
+  }
+
+  @Override
+  public String toString() {
+    return "SensorEnrichmentConfig{" +
+            "index='" + index + '\'' +
+            ", batchSize=" + batchSize +
+            ", enrichment=" + enrichment +
+            ", threatIntel=" + threatIntel +
+            '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    SensorEnrichmentConfig that = (SensorEnrichmentConfig) o;
+
+    if (getBatchSize() != that.getBatchSize()) return false;
+    if (getIndex() != null ? !getIndex().equals(that.getIndex()) : that.getIndex() != null) return false;
+    if (getEnrichment() != null ? !getEnrichment().equals(that.getEnrichment()) : that.getEnrichment() != null)
+      return false;
+    return getThreatIntel() != null ? getThreatIntel().equals(that.getThreatIntel()) : that.getThreatIntel() == null;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = getIndex() != null ? getIndex().hashCode() : 0;
+    result = 31 * result + getBatchSize();
+    result = 31 * result + (getEnrichment() != null ? getEnrichment().hashCode() : 0);
+    result = 31 * result + (getThreatIntel() != null ? getThreatIntel().hashCode() : 0);
+    return result;
+  }
+
+  public static SensorEnrichmentConfig fromBytes(byte[] config) throws IOException {
+    return JSONUtils.INSTANCE.load(new String(config), SensorEnrichmentConfig.class);
+  }
+
+  public String toJSON() throws JsonProcessingException {
+    return JSONUtils.INSTANCE.toJSON(this, true);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentUpdateConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentUpdateConfig.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentUpdateConfig.java
new file mode 100644
index 0000000..99e681a
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/SensorEnrichmentUpdateConfig.java
@@ -0,0 +1,209 @@
+/**
+ * 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.common.configuration.enrichment;
+
+import com.google.common.base.Joiner;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.metron.common.Constants;
+import org.apache.metron.common.configuration.ConfigurationsUtils;
+import org.apache.zookeeper.KeeperException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public class SensorEnrichmentUpdateConfig {
+
+  protected static final Logger _LOG = LoggerFactory.getLogger(SensorEnrichmentUpdateConfig.class);
+  public static class FieldList {
+    Type type;
+    Map<String, List<String>> fieldToEnrichmentTypes;
+
+    public Type getType() {
+      return type;
+    }
+
+    public void setType(Type type) {
+      this.type = type;
+    }
+
+    public Map<String, List<String>> getFieldToEnrichmentTypes() {
+      return fieldToEnrichmentTypes;
+    }
+
+    public void setFieldToEnrichmentTypes(Map<String, List<String>> fieldToEnrichmentTypes) {
+      this.fieldToEnrichmentTypes = fieldToEnrichmentTypes;
+    }
+  }
+  public String zkQuorum;
+  public Map<String, FieldList> sensorToFieldList;
+
+  public String getZkQuorum() {
+    return zkQuorum;
+  }
+
+  public void setZkQuorum(String zkQuorum) {
+    this.zkQuorum = zkQuorum;
+  }
+
+  public Map<String, FieldList> getSensorToFieldList() {
+    return sensorToFieldList;
+  }
+
+  public void setSensorToFieldList(Map<String, FieldList> sensorToFieldList) {
+    this.sensorToFieldList = sensorToFieldList;
+  }
+
+  public void updateSensorConfigs( ) throws Exception {
+    CuratorFramework client = ConfigurationsUtils.getClient(getZkQuorum());
+    try {
+      client.start();
+      updateSensorConfigs(new ZKSourceConfigHandler(client), sensorToFieldList);
+    }
+    finally {
+      client.close();
+    }
+  }
+
+  public static interface SourceConfigHandler {
+    SensorEnrichmentConfig readConfig(String sensor) throws Exception;
+    void persistConfig(String sensor, SensorEnrichmentConfig config) throws Exception;
+  }
+
+  public static class ZKSourceConfigHandler implements SourceConfigHandler {
+    CuratorFramework client;
+    public ZKSourceConfigHandler(CuratorFramework client) {
+      this.client = client;
+    }
+    @Override
+    public SensorEnrichmentConfig readConfig(String sensor) throws Exception {
+      SensorEnrichmentConfig sensorEnrichmentConfig = new SensorEnrichmentConfig();
+      try {
+        sensorEnrichmentConfig = SensorEnrichmentConfig.fromBytes(ConfigurationsUtils.readSensorEnrichmentConfigBytesFromZookeeper(sensor, client));
+      }catch (KeeperException.NoNodeException e) {
+        sensorEnrichmentConfig.setIndex(sensor);
+        sensorEnrichmentConfig.setBatchSize(1);
+      }
+      return sensorEnrichmentConfig;
+    }
+
+    @Override
+    public void persistConfig(String sensor, SensorEnrichmentConfig config) throws Exception {
+      ConfigurationsUtils.writeSensorEnrichmentConfigToZookeeper(sensor, config.toJSON().getBytes(), client);
+    }
+  }
+
+  public static void updateSensorConfigs( SourceConfigHandler scHandler
+                                        , Map<String, FieldList> sensorToFieldList
+                                        ) throws Exception
+  {
+    Map<String, SensorEnrichmentConfig> sourceConfigsChanged = new HashMap<>();
+    for (Map.Entry<String, FieldList> kv : sensorToFieldList.entrySet()) {
+      SensorEnrichmentConfig config = sourceConfigsChanged.get(kv.getKey());
+      if(config == null) {
+        config = scHandler.readConfig(kv.getKey());
+        if(_LOG.isDebugEnabled()) {
+          _LOG.debug(config.toJSON());
+        }
+      }
+      Map<String, List<String> > fieldMap = null;
+      Map<String, List<String> > fieldToTypeMap = null;
+      List<String> fieldList = null;
+      if(kv.getValue().type == Type.THREAT_INTEL) {
+        fieldMap = config.getThreatIntel().getFieldMap();
+        if(fieldMap!= null) {
+          fieldList = fieldMap.get(Constants.SIMPLE_HBASE_THREAT_INTEL);
+        } else {
+          fieldMap = new HashMap<>();
+        }
+        if(fieldList == null) {
+          fieldList = new ArrayList<>();
+          fieldMap.put(Constants.SIMPLE_HBASE_THREAT_INTEL, fieldList);
+        }
+        fieldToTypeMap = config.getThreatIntel().getFieldToTypeMap();
+        if(fieldToTypeMap == null) {
+          fieldToTypeMap = new HashMap<>();
+          config.getThreatIntel().setFieldToTypeMap(fieldToTypeMap);
+        }
+      }
+      else if(kv.getValue().type == Type.ENRICHMENT) {
+        fieldMap = config.getEnrichment().getFieldMap();
+        if(fieldMap!= null) {
+          fieldList = fieldMap.get(Constants.SIMPLE_HBASE_ENRICHMENT);
+        } else {
+          fieldMap = new HashMap<>();
+        }
+        if(fieldList == null) {
+          fieldList = new ArrayList<>();
+          fieldMap.put(Constants.SIMPLE_HBASE_ENRICHMENT, fieldList);
+        }
+        fieldToTypeMap = config.getEnrichment().getFieldToTypeMap();
+        if(fieldToTypeMap == null) {
+          fieldToTypeMap = new HashMap<>();
+          config.getEnrichment().setFieldToTypeMap(fieldToTypeMap);
+        }
+      }
+      if(fieldToTypeMap == null  || fieldMap == null) {
+        _LOG.debug("fieldToTypeMap is null or fieldMap is null, so skipping");
+        continue;
+      }
+      //Add the additional fields to the field list associated with the hbase adapter
+      {
+        HashSet<String> fieldSet = new HashSet<>(fieldList);
+        List<String> additionalFields = new ArrayList<>();
+        for (String field : kv.getValue().getFieldToEnrichmentTypes().keySet()) {
+          if (!fieldSet.contains(field)) {
+            additionalFields.add(field);
+          }
+        }
+        //adding only the ones that we don't already have to the field list
+        if (additionalFields.size() > 0) {
+          _LOG.debug("Adding additional fields: " + Joiner.on(',').join(additionalFields));
+          fieldList.addAll(additionalFields);
+          sourceConfigsChanged.put(kv.getKey(), config);
+        }
+      }
+      //Add the additional enrichment types to the mapping between the fields
+      {
+        for(Map.Entry<String, List<String>> fieldToType : kv.getValue().getFieldToEnrichmentTypes().entrySet()) {
+          String field = fieldToType.getKey();
+          final HashSet<String> types = new HashSet<>(fieldToType.getValue());
+          int sizeBefore = 0;
+          if(fieldToTypeMap.containsKey(field)) {
+            List<String> typeList = fieldToTypeMap.get(field);
+            sizeBefore = new HashSet<>(typeList).size();
+            types.addAll(typeList);
+          }
+          int sizeAfter = types.size();
+          boolean changed = sizeBefore != sizeAfter;
+          if(changed) {
+            fieldToTypeMap.put(field, new ArrayList<String>() {{
+                addAll(types);
+              }});
+            sourceConfigsChanged.put(kv.getKey(), config);
+          }
+        }
+      }
+    }
+    for(Map.Entry<String, SensorEnrichmentConfig> kv : sourceConfigsChanged.entrySet()) {
+      scHandler.persistConfig(kv.getKey(), kv.getValue());
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/Type.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/Type.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/Type.java
new file mode 100644
index 0000000..dc56495
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/Type.java
@@ -0,0 +1,24 @@
+/**
+ * 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.common.configuration.enrichment;
+
+public enum Type {
+   THREAT_INTEL
+  ,ENRICHMENT
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatIntelConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatIntelConfig.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatIntelConfig.java
new file mode 100644
index 0000000..3b34a0a
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatIntelConfig.java
@@ -0,0 +1,59 @@
+/**
+ * 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.common.configuration.enrichment.threatintel;
+
+import org.apache.metron.common.configuration.enrichment.EnrichmentConfig;
+
+public class ThreatIntelConfig extends EnrichmentConfig {
+  private ThreatTriageConfig triageConfig = new ThreatTriageConfig();
+
+  public ThreatTriageConfig getTriageConfig() {
+    return triageConfig;
+  }
+
+  public void setTriageConfig(ThreatTriageConfig triageConfig) {
+    this.triageConfig = triageConfig;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    if (!super.equals(o)) return false;
+
+    ThreatIntelConfig that = (ThreatIntelConfig) o;
+
+    return getTriageConfig() != null ? getTriageConfig().equals(that.getTriageConfig()) : that.getTriageConfig() == null;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + (getTriageConfig() != null ? getTriageConfig().hashCode() : 0);
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "ThreatIntelConfig{" +
+            "triageConfig=" + triageConfig +
+            '}';
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatTriageConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatTriageConfig.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatTriageConfig.java
new file mode 100644
index 0000000..9dea047
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/enrichment/threatintel/ThreatTriageConfig.java
@@ -0,0 +1,101 @@
+/**
+ * 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.common.configuration.enrichment.threatintel;
+
+import com.google.common.base.Joiner;
+import org.apache.metron.common.aggregator.Aggregator;
+import org.apache.metron.common.aggregator.Aggregators;
+import org.apache.metron.common.query.PredicateProcessor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ThreatTriageConfig {
+  private Map<String, Number> riskLevelRules = new HashMap<>();
+  private Aggregator aggregator = Aggregators.MAX;
+  private Map<String, Object> aggregationConfig = new HashMap<>();
+
+  public Map<String, Number> getRiskLevelRules() {
+    return riskLevelRules;
+  }
+
+  public void setRiskLevelRules(Map<String, Number> riskLevelRules) {
+    this.riskLevelRules = riskLevelRules;
+    PredicateProcessor processor = new PredicateProcessor();
+    for(String rule : riskLevelRules.keySet()) {
+      processor.validate(rule);
+    }
+  }
+
+  public Aggregator getAggregator() {
+    return aggregator;
+  }
+
+  public void setAggregator(String aggregator) {
+    try {
+      this.aggregator = Aggregators.valueOf(aggregator);
+    }
+    catch(IllegalArgumentException iae) {
+      throw new IllegalArgumentException("Unable to load aggregator of " + aggregator
+                                        + ".  Valid aggregators are " + Joiner.on(',').join(Aggregators.values())
+                                        );
+    }
+  }
+
+  public Map<String, Object> getAggregationConfig() {
+    return aggregationConfig;
+  }
+
+  public void setAggregationConfig(Map<String, Object> aggregationConfig) {
+    this.aggregationConfig = aggregationConfig;
+  }
+
+  @Override
+  public String toString() {
+    return "ThreatTriageConfig{" +
+            "riskLevelRules=" + riskLevelRules +
+            ", aggregator=" + aggregator +
+            ", aggregationConfig=" + aggregationConfig +
+            '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ThreatTriageConfig that = (ThreatTriageConfig) o;
+
+    if (getRiskLevelRules() != null ? !getRiskLevelRules().equals(that.getRiskLevelRules()) : that.getRiskLevelRules() != null)
+      return false;
+    if (getAggregator() != null ? !getAggregator().equals(that.getAggregator()) : that.getAggregator() != null)
+      return false;
+    return getAggregationConfig() != null ? getAggregationConfig().equals(that.getAggregationConfig()) : that.getAggregationConfig() == null;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = getRiskLevelRules() != null ? getRiskLevelRules().hashCode() : 0;
+    result = 31 * result + (getAggregator() != null ? getAggregator().hashCode() : 0);
+    result = 31 * result + (getAggregationConfig() != null ? getAggregationConfig().hashCode() : 0);
+    return result;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/BooleanOp.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/BooleanOp.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/BooleanOp.java
new file mode 100644
index 0000000..cedc9a3
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/BooleanOp.java
@@ -0,0 +1,23 @@
+/**
+ * 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.common.query;
+
+public interface BooleanOp {
+  boolean op(boolean left, boolean right);
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ErrorListener.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ErrorListener.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ErrorListener.java
new file mode 100644
index 0000000..883dee2
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ErrorListener.java
@@ -0,0 +1,50 @@
+/**
+ * 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.common.query;
+
+import org.antlr.v4.runtime.ANTLRErrorListener;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.atn.ATNConfigSet;
+import org.antlr.v4.runtime.dfa.DFA;
+
+import java.util.BitSet;
+
+public class ErrorListener implements ANTLRErrorListener {
+  @Override
+  public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
+    throw new ParseException("Syntax error @ " + line + ":" + charPositionInLine+ " " + msg, e);
+  }
+
+
+  @Override
+  public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, BitSet ambigAlts, ATNConfigSet configs) {
+  }
+
+
+  @Override
+  public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) {
+  }
+
+
+  @Override
+  public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, ATNConfigSet configs) {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/FunctionMarker.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/FunctionMarker.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/FunctionMarker.java
new file mode 100644
index 0000000..aefbbbf
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/FunctionMarker.java
@@ -0,0 +1,21 @@
+/**
+ * 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.common.query;
+
+public class FunctionMarker { }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/LogicalFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/LogicalFunctions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/LogicalFunctions.java
new file mode 100644
index 0000000..77aa173
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/LogicalFunctions.java
@@ -0,0 +1,99 @@
+/**
+ * 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.common.query;
+
+import com.google.common.base.Function;
+import org.apache.commons.net.util.SubnetUtils;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public enum LogicalFunctions implements Function<List<String>, Boolean> {
+  IS_EMPTY ( list -> {
+    if(list.size() == 0) {
+      throw new IllegalStateException("IS_EMPTY expects one string arg");
+    }
+    String val = list.get(0);
+    return val == null || val.isEmpty() ? true:false;
+  })
+  ,IN_SUBNET( list -> {
+    if(list.size() < 2) {
+      throw new IllegalStateException("IN_SUBNET expects at least two args: [ip, cidr1, cidr2, ...]"
+                                     + " where cidr is the subnet mask in cidr form"
+                                     );
+    }
+    String ip = list.get(0);
+    if(ip == null) {
+      return false;
+    }
+    boolean inSubnet = false;
+    for(int i = 1;i < list.size() && !inSubnet;++i) {
+      String cidr = list.get(1);
+      if(cidr == null) {
+        continue;
+      }
+      inSubnet |= new SubnetUtils(cidr).getInfo().isInRange(ip);
+    }
+
+    return inSubnet;
+  })
+  ,STARTS_WITH( list -> {
+    if(list.size() < 2) {
+      throw new IllegalStateException("STARTS_WITH expects two args: [string, prefix] where prefix is the string fragment that the string should start with");
+    }
+    String prefix = list.get(1);
+    String str = list.get(0);
+    if(str == null || prefix == null) {
+      return false;
+    }
+    return str.startsWith(prefix);
+  })
+  ,ENDS_WITH( list -> {
+    if(list.size() < 2) {
+      throw new IllegalStateException("ENDS_WITH expects two args: [string, suffix] where suffix is the string fragment that the string should end with");
+    }
+    String prefix = list.get(1);
+    String str = list.get(0);
+    if(str == null || prefix == null) {
+      return false;
+    }
+    return str.endsWith(prefix);
+  })
+  ,REGEXP_MATCH( list -> {
+     if(list.size() < 2) {
+      throw new IllegalStateException("REGEXP_MATCH expects two args: [string, pattern] where pattern is a regexp pattern");
+    }
+    String pattern = list.get(1);
+    String str = list.get(0);
+    if(str == null || pattern == null) {
+      return false;
+    }
+    return str.matches(pattern);
+  })
+  ;
+  Function<List<String>, Boolean> func;
+  LogicalFunctions(Function<List<String>, Boolean> func) {
+    this.func = func;
+  }
+  @Nullable
+  @Override
+  public Boolean apply(@Nullable List<String> input) {
+    return func.apply(input);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/MapVariableResolver.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/MapVariableResolver.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/MapVariableResolver.java
new file mode 100644
index 0000000..ad1f810
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/MapVariableResolver.java
@@ -0,0 +1,33 @@
+/**
+ * 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.common.query;
+
+import java.util.Map;
+
+public class MapVariableResolver implements VariableResolver {
+  Map variableMapping;
+  public MapVariableResolver(Map variableMapping) {
+    this.variableMapping = variableMapping;
+  }
+  @Override
+  public String resolve(String variable) {
+    Object o = variableMapping.get(variable);
+    return o == null?null:o.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ParseException.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ParseException.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ParseException.java
new file mode 100644
index 0000000..7d658f0
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/ParseException.java
@@ -0,0 +1,28 @@
+/**
+ * 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.common.query;
+
+public class ParseException extends RuntimeException {
+  public ParseException(String reason) {
+    super(reason);
+  }
+  public ParseException(String reason, Throwable t) {
+    super(reason, t);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateProcessor.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateProcessor.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateProcessor.java
new file mode 100644
index 0000000..6499319
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateProcessor.java
@@ -0,0 +1,59 @@
+/**
+ * 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.common.query;
+
+import org.antlr.v4.runtime.*;
+import org.apache.metron.common.query.generated.*;
+
+
+public class PredicateProcessor {
+  public boolean parse(String rule, VariableResolver resolver) {
+    ANTLRInputStream input = new ANTLRInputStream(rule);
+    PredicateLexer lexer = new PredicateLexer(input);
+    lexer.removeErrorListeners();
+    lexer.addErrorListener(new ErrorListener());
+    TokenStream tokens = new CommonTokenStream(lexer);
+    PredicateParser parser = new PredicateParser(tokens);
+
+    QueryCompiler treeBuilder = new QueryCompiler(resolver);
+    parser.addParseListener(treeBuilder);
+    parser.removeErrorListeners();
+    parser.addErrorListener(new ErrorListener());
+    parser.single_rule();
+    return treeBuilder.getResult();
+  }
+
+  public boolean validate(String rule) throws ParseException {
+    return validate(rule, true);
+  }
+  public boolean validate(String rule, boolean throwException) throws ParseException {
+    try {
+      parse(rule, x -> null);
+      return true;
+    }
+    catch(Throwable t) {
+      if(throwException) {
+        throw new ParseException("Unable to parse " + rule + ": " + t.getMessage(), t);
+      }
+      else {
+        return false;
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateToken.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateToken.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateToken.java
new file mode 100644
index 0000000..54ac17d
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/PredicateToken.java
@@ -0,0 +1,58 @@
+/**
+ * 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.common.query;
+
+public class PredicateToken<T> {
+  T value;
+  Class<T> underlyingType;
+  public PredicateToken(T value, Class<T> clazz) {
+    this.value = value;
+    this.underlyingType = clazz;
+  }
+  public T getValue() {
+    return value;
+  }
+  public Class<T> getUnderlyingType() {
+    return underlyingType;
+  }
+
+  @Override
+  public String toString() {
+    return value.toString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    PredicateToken<?> predicateToken = (PredicateToken<?>) o;
+
+    if (getValue() != null ? !getValue().equals(predicateToken.getValue()) : predicateToken.getValue() != null) return false;
+    return getUnderlyingType() != null ? getUnderlyingType().equals(predicateToken.getUnderlyingType()) : predicateToken.getUnderlyingType() == null;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = getValue() != null ? getValue().hashCode() : 0;
+    result = 31 * result + (getUnderlyingType() != null ? getUnderlyingType().hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/QueryCompiler.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/QueryCompiler.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/QueryCompiler.java
new file mode 100644
index 0000000..6351eed
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/QueryCompiler.java
@@ -0,0 +1,288 @@
+/**
+ * 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.common.query;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import org.apache.metron.common.query.generated.PredicateBaseListener;
+import org.apache.metron.common.query.generated.PredicateParser;
+
+import java.util.*;
+
+class QueryCompiler extends PredicateBaseListener {
+  private VariableResolver resolver = null;
+  private Stack<PredicateToken> tokenStack = new Stack<>();
+
+  public QueryCompiler(VariableResolver resolver) {
+    this.resolver = resolver;
+  }
+
+  @Override
+  public void enterSingle_rule(org.apache.metron.common.query.generated.PredicateParser.Single_ruleContext ctx) {
+    tokenStack.clear();
+  }
+
+  @Override
+  public void exitSingle_rule(org.apache.metron.common.query.generated.PredicateParser.Single_ruleContext ctx) {
+  }
+
+  @Override
+  public void exitLogicalExpressionAnd(PredicateParser.LogicalExpressionAndContext ctx) {
+    PredicateToken<?> left = popStack();
+    PredicateToken<?> right = popStack();
+    tokenStack.push(new PredicateToken<>(booleanOp(left, right, (l, r) -> l && r, "&&"), Boolean.class));
+  }
+
+  @Override
+  public void exitLogicalExpressionOr(PredicateParser.LogicalExpressionOrContext ctx) {
+    PredicateToken<?> left = popStack();
+    PredicateToken<?> right = popStack();
+
+    tokenStack.push(new PredicateToken<>(booleanOp(left, right, (l, r) -> l || r, "||"), Boolean.class));
+  }
+
+  private boolean booleanOp(PredicateToken<?> left, PredicateToken<?> right, BooleanOp op, String opName)
+  {
+    if(left.getUnderlyingType().equals(right.getUnderlyingType()) && left.getUnderlyingType().equals(Boolean.class)) {
+      Boolean l = (Boolean) left.getValue();
+      Boolean r = (Boolean) right.getValue();
+      if(l == null || r == null) {
+        throw new ParseException("Unable to operate on " + left.getValue()  + " " + opName + " " + right.getValue() + ", null value");
+      }
+      return op.op(l, r);
+    }
+    else {
+      throw new ParseException("Unable to operate on " + left.getValue()  + " " + opName + " " + right.getValue() + ", bad types");
+    }
+  }
+
+
+  @Override
+  public void exitLogicalConst(PredicateParser.LogicalConstContext ctx) {
+    Boolean b = null;
+    switch(ctx.getText().toUpperCase()) {
+      case "TRUE":
+        b = true;
+        break;
+      case "FALSE":
+        b = false;
+        break;
+      default:
+        throw new ParseException("Unable to process " + ctx.getText() + " as a boolean constant");
+    }
+    tokenStack.push(new PredicateToken<>(b, Boolean.class));
+  }
+
+  @Override
+  public void exitComparisonExpressionWithOperator(PredicateParser.ComparisonExpressionWithOperatorContext ctx) {
+    boolean isEqualsOp = ctx.getChild(1).getText().equals("==");
+    PredicateToken<?> left = popStack();
+    PredicateToken<?> right = popStack();
+    if(left.getUnderlyingType().equals(right.getUnderlyingType())) {
+      boolean isEquals = left.equals(right);
+      tokenStack.push(new PredicateToken<>(isEqualsOp?isEquals:!isEquals, Boolean.class));
+    }
+    else {
+      throw new ParseException("Unable to compare " + left.getValue() + " " + ctx.getText() + " " + right.getValue());
+    }
+  }
+
+  public PredicateToken<?> popStack() {
+    if(tokenStack.empty()) {
+      throw new ParseException("Unable to pop an empty stack");
+    }
+    return tokenStack.pop();
+  }
+
+  @Override
+  public void exitLogicalVariable(PredicateParser.LogicalVariableContext ctx) {
+    tokenStack.push(new PredicateToken<>(resolver.resolve(ctx.getText()), String.class));
+  }
+
+
+  @Override
+  public void exitStringLiteral(PredicateParser.StringLiteralContext ctx) {
+    String val = ctx.getText();
+    tokenStack.push(new PredicateToken<>(val.substring(1, val.length() - 1), String.class));
+  }
+
+
+  @Override
+  public void enterList_entity(PredicateParser.List_entityContext ctx) {
+    tokenStack.push(new PredicateToken<>(new FunctionMarker(), FunctionMarker.class));
+  }
+
+
+  @Override
+  public void exitList_entity(PredicateParser.List_entityContext ctx) {
+    Set<String> inSet = new HashSet<>();
+    while(true) {
+      PredicateToken<?> token = popStack();
+      if(token.getUnderlyingType().equals(FunctionMarker.class)) {
+        break;
+      }
+      else {
+        inSet.add((String)token.getValue());
+      }
+    }
+    tokenStack.push(new PredicateToken<>(inSet, Set.class));
+  }
+
+
+  @Override
+  public void enterFunc_args(PredicateParser.Func_argsContext ctx) {
+    tokenStack.push(new PredicateToken<>(new FunctionMarker(), FunctionMarker.class));
+  }
+
+
+  @Override
+  public void exitFunc_args(PredicateParser.Func_argsContext ctx) {
+    LinkedList<String> args = new LinkedList<>();
+    while(true) {
+      PredicateToken<?> token = popStack();
+      if(token.getUnderlyingType().equals(FunctionMarker.class)) {
+        break;
+      }
+      else {
+        args.addFirst((String)token.getValue());
+      }
+    }
+    tokenStack.push(new PredicateToken<>(args, List.class));
+  }
+
+  @Override
+  public void exitInExpression(PredicateParser.InExpressionContext ctx) {
+    PredicateToken<?> left = popStack();
+    PredicateToken<?> right = popStack();
+    String key = null;
+    Set<String> set = null;
+    if(left.getUnderlyingType().equals(Set.class)) {
+      set = (Set<String>) left.getValue();
+    }
+    else {
+      throw new ParseException("Unable to process in clause because " + left.getValue() + " is not a set");
+    }
+    if(right.getUnderlyingType().equals(String.class)) {
+      key = (String) right.getValue();
+    }
+    else {
+      throw new ParseException("Unable to process in clause because " + right.getValue() + " is not a string");
+    }
+    tokenStack.push(new PredicateToken<>(set.contains(key), Boolean.class));
+  }
+
+  @Override
+  public void exitNInExpression(PredicateParser.NInExpressionContext ctx) {
+    PredicateToken<?> left = popStack();
+    PredicateToken<?> right = popStack();
+    String key = null;
+    Set<String> set = null;
+    if(left.getUnderlyingType().equals(Set.class)) {
+      set = (Set<String>) left.getValue();
+    }
+    else {
+      throw new ParseException("Unable to process in clause because " + left.getValue() + " is not a set");
+    }
+    if(right.getUnderlyingType().equals(String.class)) {
+      key = (String) right.getValue();
+    }
+    else {
+      throw new ParseException("Unable to process in clause because " + right.getValue() + " is not a string");
+    }
+    tokenStack.push(new PredicateToken<>(!set.contains(key), Boolean.class));
+  }
+
+
+  @Override
+  public void exitLogicalFunc(PredicateParser.LogicalFuncContext ctx) {
+    String funcName = ctx.getChild(0).getText();
+    Function<List<String>, Boolean> func;
+    try {
+      func = LogicalFunctions.valueOf(funcName);
+    }
+    catch(IllegalArgumentException iae) {
+      throw new ParseException("Unable to find logical function " + funcName + ".  Valid functions are "
+              + Joiner.on(',').join(LogicalFunctions.values())
+      );
+    }
+    PredicateToken<?> left = popStack();
+    List<String> argList = null;
+    if(left.getUnderlyingType().equals(List.class)) {
+      argList = (List<String>) left.getValue();
+    }
+    else {
+      throw new ParseException("Unable to process in clause because " + left.getValue() + " is not a set");
+    }
+    Boolean result = func.apply(argList);
+    tokenStack.push(new PredicateToken<>(result, Boolean.class));
+  }
+
+  @Override
+  public void exitStringFunc(PredicateParser.StringFuncContext ctx) {
+    String funcName = ctx.getChild(0).getText();
+    Function<List<String>, String> func;
+    try {
+      func = StringFunctions.valueOf(funcName);
+    }
+    catch(IllegalArgumentException iae) {
+      throw new ParseException("Unable to find string function " + funcName + ".  Valid functions are "
+              + Joiner.on(',').join(StringFunctions.values())
+      );
+    }
+    PredicateToken<?> left = popStack();
+    List<String> argList = null;
+    if(left.getUnderlyingType().equals(List.class)) {
+      argList = (List<String>) left.getValue();
+    }
+    else {
+      throw new ParseException("Unable to process in clause because " + left.getValue() + " is not a set");
+    }
+    String result = func.apply(argList);
+    tokenStack.push(new PredicateToken<>(result, String.class));
+  }
+
+  @Override
+  public void exitExistsFunc(PredicateParser.ExistsFuncContext ctx) {
+    String variable = ctx.getChild(2).getText();
+    boolean exists = resolver.resolve(variable) != null;
+    tokenStack.push(new PredicateToken<>(exists, Boolean.class));
+  }
+
+  @Override
+  public void exitNotFunc(PredicateParser.NotFuncContext ctx) {
+    PredicateToken<Boolean> arg = (PredicateToken<Boolean>) popStack();
+    tokenStack.push(new PredicateToken<>(!arg.getValue(), Boolean.class));
+  }
+
+  public boolean getResult() throws ParseException {
+    if(tokenStack.empty()) {
+      throw new ParseException("Invalid predicate: Empty stack.");
+    }
+    PredicateToken<?> token = popStack();
+    if(token.getUnderlyingType().equals(Boolean.class) && tokenStack.empty()) {
+      return (Boolean)token.getValue();
+    }
+    if(tokenStack.empty()) {
+      throw new ParseException("Invalid parse, stack not empty: " + Joiner.on(',').join(tokenStack));
+    }
+    else {
+      throw new ParseException("Invalid parse, found " + token + " but expected boolean");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/StringFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/StringFunctions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/StringFunctions.java
new file mode 100644
index 0000000..911063e
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/StringFunctions.java
@@ -0,0 +1,42 @@
+/**
+ * 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.common.query;
+
+
+import com.google.common.base.Function;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public enum StringFunctions implements Function<List<String>, String> {
+  TO_LOWER(strings -> strings.get(0)==null?null:strings.get(0).toLowerCase())
+  ,TO_UPPER(strings -> strings.get(0) == null?null:strings.get(0).toUpperCase())
+  ,TRIM(strings -> strings.get(0) == null?null:strings.get(0).trim())
+  ;
+  Function<List<String>, String> func;
+  StringFunctions(Function<List<String>, String> func) {
+    this.func = func;
+  }
+
+  @Nullable
+  @Override
+  public String apply(@Nullable List<String> input) {
+    return func.apply(input);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/VariableResolver.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/VariableResolver.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/VariableResolver.java
new file mode 100644
index 0000000..d0a6625
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/VariableResolver.java
@@ -0,0 +1,23 @@
+/**
+ * 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.common.query;
+
+public interface VariableResolver {
+  String resolve(String variable);
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateBaseListener.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateBaseListener.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateBaseListener.java
new file mode 100644
index 0000000..ca7ac84
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateBaseListener.java
@@ -0,0 +1,336 @@
+// Generated from org/apache/metron/common/query/generated/Predicate.g4 by ANTLR 4.5
+package org.apache.metron.common.query.generated;
+
+//CHECKSTYLE:OFF
+/**
+ * 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.
+ */
+
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ErrorNode;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ * This class provides an empty implementation of {@link PredicateListener},
+ * which can be extended to create a listener which only needs to handle a subset
+ * of the available methods.
+ */
+public class PredicateBaseListener implements PredicateListener {
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterSingle_rule(PredicateParser.Single_ruleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitSingle_rule(PredicateParser.Single_ruleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalEntity(PredicateParser.LogicalEntityContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalEntity(PredicateParser.LogicalEntityContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterComparisonExpression(PredicateParser.ComparisonExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitComparisonExpression(PredicateParser.ComparisonExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalExpressionInParen(PredicateParser.LogicalExpressionInParenContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalExpressionInParen(PredicateParser.LogicalExpressionInParenContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterNotFunc(PredicateParser.NotFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitNotFunc(PredicateParser.NotFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalExpressionAnd(PredicateParser.LogicalExpressionAndContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalExpressionAnd(PredicateParser.LogicalExpressionAndContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalExpressionOr(PredicateParser.LogicalExpressionOrContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalExpressionOr(PredicateParser.LogicalExpressionOrContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterComparisonExpressionWithOperator(PredicateParser.ComparisonExpressionWithOperatorContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitComparisonExpressionWithOperator(PredicateParser.ComparisonExpressionWithOperatorContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterInExpression(PredicateParser.InExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitInExpression(PredicateParser.InExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterNInExpression(PredicateParser.NInExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitNInExpression(PredicateParser.NInExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterComparisonExpressionParens(PredicateParser.ComparisonExpressionParensContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitComparisonExpressionParens(PredicateParser.ComparisonExpressionParensContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalConst(PredicateParser.LogicalConstContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalConst(PredicateParser.LogicalConstContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterExistsFunc(PredicateParser.ExistsFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitExistsFunc(PredicateParser.ExistsFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalFunc(PredicateParser.LogicalFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalFunc(PredicateParser.LogicalFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterList_entity(PredicateParser.List_entityContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitList_entity(PredicateParser.List_entityContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterFunc_args(PredicateParser.Func_argsContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitFunc_args(PredicateParser.Func_argsContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterOp_list(PredicateParser.Op_listContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitOp_list(PredicateParser.Op_listContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterStringLiteral(PredicateParser.StringLiteralContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitStringLiteral(PredicateParser.StringLiteralContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalVariable(PredicateParser.LogicalVariableContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalVariable(PredicateParser.LogicalVariableContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterStringFunc(PredicateParser.StringFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitStringFunc(PredicateParser.StringFuncContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterIdentifierOperand(PredicateParser.IdentifierOperandContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitIdentifierOperand(PredicateParser.IdentifierOperandContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLogicalConstComparison(PredicateParser.LogicalConstComparisonContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLogicalConstComparison(PredicateParser.LogicalConstComparisonContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterComparisonOp(PredicateParser.ComparisonOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitComparisonOp(PredicateParser.ComparisonOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterEveryRule(ParserRuleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitEveryRule(ParserRuleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void visitTerminal(TerminalNode node) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void visitErrorNode(ErrorNode node) { }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateLexer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateLexer.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateLexer.java
new file mode 100644
index 0000000..ebc0b00
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateLexer.java
@@ -0,0 +1,178 @@
+// Generated from org/apache/metron/common/query/generated/Predicate.g4 by ANTLR 4.5
+package org.apache.metron.common.query.generated;
+
+//CHECKSTYLE:OFF
+/**
+ * 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.
+ */
+
+import org.antlr.v4.runtime.Lexer;
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.TokenStream;
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.atn.*;
+import org.antlr.v4.runtime.dfa.DFA;
+import org.antlr.v4.runtime.misc.*;
+
+@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
+public class PredicateLexer extends Lexer {
+	static { RuntimeMetaData.checkVersion("4.5", RuntimeMetaData.VERSION); }
+
+	protected static final DFA[] _decisionToDFA;
+	protected static final PredictionContextCache _sharedContextCache =
+		new PredictionContextCache();
+	public static final int
+		AND=1, OR=2, NOT=3, TRUE=4, FALSE=5, EQ=6, NEQ=7, COMMA=8, LBRACKET=9, 
+		RBRACKET=10, LPAREN=11, RPAREN=12, IN=13, NIN=14, EXISTS=15, IDENTIFIER=16, 
+		STRING_LITERAL=17, SEMI=18, COMMENT=19, WS=20;
+	public static String[] modeNames = {
+		"DEFAULT_MODE"
+	};
+
+	public static final String[] ruleNames = {
+		"AND", "OR", "NOT", "TRUE", "FALSE", "EQ", "NEQ", "COMMA", "LBRACKET", 
+		"RBRACKET", "LPAREN", "RPAREN", "IN", "NIN", "EXISTS", "IDENTIFIER", "SCHAR", 
+		"STRING_LITERAL", "SEMI", "COMMENT", "WS"
+	};
+
+	private static final String[] _LITERAL_NAMES = {
+		null, null, null, null, null, null, "'=='", "'!='", "','", "'['", "']'", 
+		"'('", "')'", "'in'", "'not in'", "'exists'", null, null, "';'"
+	};
+	private static final String[] _SYMBOLIC_NAMES = {
+		null, "AND", "OR", "NOT", "TRUE", "FALSE", "EQ", "NEQ", "COMMA", "LBRACKET", 
+		"RBRACKET", "LPAREN", "RPAREN", "IN", "NIN", "EXISTS", "IDENTIFIER", "STRING_LITERAL", 
+		"SEMI", "COMMENT", "WS"
+	};
+	public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
+
+	/**
+	 * @deprecated Use {@link #VOCABULARY} instead.
+	 */
+	@Deprecated
+	public static final String[] tokenNames;
+	static {
+		tokenNames = new String[_SYMBOLIC_NAMES.length];
+		for (int i = 0; i < tokenNames.length; i++) {
+			tokenNames[i] = VOCABULARY.getLiteralName(i);
+			if (tokenNames[i] == null) {
+				tokenNames[i] = VOCABULARY.getSymbolicName(i);
+			}
+
+			if (tokenNames[i] == null) {
+				tokenNames[i] = "<INVALID>";
+			}
+		}
+	}
+
+	@Override
+	@Deprecated
+	public String[] getTokenNames() {
+		return tokenNames;
+	}
+
+	@Override
+
+	public Vocabulary getVocabulary() {
+		return VOCABULARY;
+	}
+
+
+	public PredicateLexer(CharStream input) {
+		super(input);
+		_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
+	}
+
+	@Override
+	public String getGrammarFileName() { return "Predicate.g4"; }
+
+	@Override
+	public String[] getRuleNames() { return ruleNames; }
+
+	@Override
+	public String getSerializedATN() { return _serializedATN; }
+
+	@Override
+	public String[] getModeNames() { return modeNames; }
+
+	@Override
+	public ATN getATN() { return _ATN; }
+
+	public static final String _serializedATN =
+		"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\26\u00af\b\1\4\2"+
+		"\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4"+
+		"\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22"+
+		"\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\3\2\3\2\3\2\3\2\3\2\3\2"+
+		"\3\2\3\2\5\2\66\n\2\3\3\3\3\3\3\3\3\3\3\3\3\5\3>\n\3\3\4\3\4\3\4\3\4\3"+
+		"\4\3\4\5\4F\n\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\5\5P\n\5\3\6\3\6\3\6\3"+
+		"\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6\\\n\6\3\7\3\7\3\7\3\b\3\b\3\b\3\t\3\t\3"+
+		"\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16\3\16\3\17\3\17\3\17\3\17\3"+
+		"\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\21\3\21\7\21\u0081"+
+		"\n\21\f\21\16\21\u0084\13\21\3\22\3\22\3\23\3\23\7\23\u008a\n\23\f\23"+
+		"\16\23\u008d\13\23\3\23\3\23\3\23\7\23\u0092\n\23\f\23\16\23\u0095\13"+
+		"\23\3\23\5\23\u0098\n\23\3\24\3\24\3\25\3\25\3\25\3\25\6\25\u00a0\n\25"+
+		"\r\25\16\25\u00a1\3\25\5\25\u00a5\n\25\3\25\3\25\3\26\6\26\u00aa\n\26"+
+		"\r\26\16\26\u00ab\3\26\3\26\3\u00a1\2\27\3\3\5\4\7\5\t\6\13\7\r\b\17\t"+
+		"\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\2%\23\'\24)\25+\26"+
+		"\3\2\7\5\2C\\aac|\b\2\60\60\62;C\\^^aac|\7\2\f\f\17\17$$))^^\3\3\f\f\5"+
+		"\2\13\f\16\17\"\"\u00ba\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2"+
+		"\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25"+
+		"\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2"+
+		"\2\2\2!\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\3\65\3\2"+
+		"\2\2\5=\3\2\2\2\7E\3\2\2\2\tO\3\2\2\2\13[\3\2\2\2\r]\3\2\2\2\17`\3\2\2"+
+		"\2\21c\3\2\2\2\23e\3\2\2\2\25g\3\2\2\2\27i\3\2\2\2\31k\3\2\2\2\33m\3\2"+
+		"\2\2\35p\3\2\2\2\37w\3\2\2\2!~\3\2\2\2#\u0085\3\2\2\2%\u0097\3\2\2\2\'"+
+		"\u0099\3\2\2\2)\u009b\3\2\2\2+\u00a9\3\2\2\2-.\7c\2\2./\7p\2\2/\66\7f"+
+		"\2\2\60\61\7(\2\2\61\66\7(\2\2\62\63\7C\2\2\63\64\7P\2\2\64\66\7F\2\2"+
+		"\65-\3\2\2\2\65\60\3\2\2\2\65\62\3\2\2\2\66\4\3\2\2\2\678\7q\2\28>\7t"+
+		"\2\29:\7~\2\2:>\7~\2\2;<\7Q\2\2<>\7T\2\2=\67\3\2\2\2=9\3\2\2\2=;\3\2\2"+
+		"\2>\6\3\2\2\2?@\7p\2\2@A\7q\2\2AF\7v\2\2BC\7P\2\2CD\7Q\2\2DF\7V\2\2E?"+
+		"\3\2\2\2EB\3\2\2\2F\b\3\2\2\2GH\7v\2\2HI\7t\2\2IJ\7w\2\2JP\7g\2\2KL\7"+
+		"V\2\2LM\7T\2\2MN\7W\2\2NP\7G\2\2OG\3\2\2\2OK\3\2\2\2P\n\3\2\2\2QR\7h\2"+
+		"\2RS\7c\2\2ST\7n\2\2TU\7u\2\2U\\\7g\2\2VW\7H\2\2WX\7C\2\2XY\7N\2\2YZ\7"+
+		"U\2\2Z\\\7G\2\2[Q\3\2\2\2[V\3\2\2\2\\\f\3\2\2\2]^\7?\2\2^_\7?\2\2_\16"+
+		"\3\2\2\2`a\7#\2\2ab\7?\2\2b\20\3\2\2\2cd\7.\2\2d\22\3\2\2\2ef\7]\2\2f"+
+		"\24\3\2\2\2gh\7_\2\2h\26\3\2\2\2ij\7*\2\2j\30\3\2\2\2kl\7+\2\2l\32\3\2"+
+		"\2\2mn\7k\2\2no\7p\2\2o\34\3\2\2\2pq\7p\2\2qr\7q\2\2rs\7v\2\2st\7\"\2"+
+		"\2tu\7k\2\2uv\7p\2\2v\36\3\2\2\2wx\7g\2\2xy\7z\2\2yz\7k\2\2z{\7u\2\2{"+
+		"|\7v\2\2|}\7u\2\2} \3\2\2\2~\u0082\t\2\2\2\177\u0081\t\3\2\2\u0080\177"+
+		"\3\2\2\2\u0081\u0084\3\2\2\2\u0082\u0080\3\2\2\2\u0082\u0083\3\2\2\2\u0083"+
+		"\"\3\2\2\2\u0084\u0082\3\2\2\2\u0085\u0086\n\4\2\2\u0086$\3\2\2\2\u0087"+
+		"\u008b\7$\2\2\u0088\u008a\5#\22\2\u0089\u0088\3\2\2\2\u008a\u008d\3\2"+
+		"\2\2\u008b\u0089\3\2\2\2\u008b\u008c\3\2\2\2\u008c\u008e\3\2\2\2\u008d"+
+		"\u008b\3\2\2\2\u008e\u0098\7$\2\2\u008f\u0093\7)\2\2\u0090\u0092\5#\22"+
+		"\2\u0091\u0090\3\2\2\2\u0092\u0095\3\2\2\2\u0093\u0091\3\2\2\2\u0093\u0094"+
+		"\3\2\2\2\u0094\u0096\3\2\2\2\u0095\u0093\3\2\2\2\u0096\u0098\7)\2\2\u0097"+
+		"\u0087\3\2\2\2\u0097\u008f\3\2\2\2\u0098&\3\2\2\2\u0099\u009a\7=\2\2\u009a"+
+		"(\3\2\2\2\u009b\u009c\7\61\2\2\u009c\u009d\7\61\2\2\u009d\u009f\3\2\2"+
+		"\2\u009e\u00a0\13\2\2\2\u009f\u009e\3\2\2\2\u00a0\u00a1\3\2\2\2\u00a1"+
+		"\u00a2\3\2\2\2\u00a1\u009f\3\2\2\2\u00a2\u00a4\3\2\2\2\u00a3\u00a5\t\5"+
+		"\2\2\u00a4\u00a3\3\2\2\2\u00a5\u00a6\3\2\2\2\u00a6\u00a7\b\25\2\2\u00a7"+
+		"*\3\2\2\2\u00a8\u00aa\t\6\2\2\u00a9\u00a8\3\2\2\2\u00aa\u00ab\3\2\2\2"+
+		"\u00ab\u00a9\3\2\2\2\u00ab\u00ac\3\2\2\2\u00ac\u00ad\3\2\2\2\u00ad\u00ae"+
+		"\b\26\2\2\u00ae,\3\2\2\2\17\2\65=EO[\u0082\u008b\u0093\u0097\u00a1\u00a4"+
+		"\u00ab\3\b\2\2";
+	public static final ATN _ATN =
+		new ATNDeserializer().deserialize(_serializedATN.toCharArray());
+	static {
+		_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
+		for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
+			_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/deed21e6/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateListener.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateListener.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateListener.java
new file mode 100644
index 0000000..6a8b669
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/query/generated/PredicateListener.java
@@ -0,0 +1,299 @@
+// Generated from org/apache/metron/common/query/generated/Predicate.g4 by ANTLR 4.5
+package org.apache.metron.common.query.generated;
+
+//CHECKSTYLE:OFF
+/**
+ * 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.
+ */
+
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ParseTreeListener;
+
+/**
+ * This interface defines a complete listener for a parse tree produced by
+ * {@link PredicateParser}.
+ */
+public interface PredicateListener extends ParseTreeListener {
+	/**
+	 * Enter a parse tree produced by {@link PredicateParser#single_rule}.
+	 * @param ctx the parse tree
+	 */
+	void enterSingle_rule(PredicateParser.Single_ruleContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link PredicateParser#single_rule}.
+	 * @param ctx the parse tree
+	 */
+	void exitSingle_rule(PredicateParser.Single_ruleContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalEntity}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalEntity(PredicateParser.LogicalEntityContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalEntity}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalEntity(PredicateParser.LogicalEntityContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code ComparisonExpression}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterComparisonExpression(PredicateParser.ComparisonExpressionContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code ComparisonExpression}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitComparisonExpression(PredicateParser.ComparisonExpressionContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalExpressionInParen}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalExpressionInParen(PredicateParser.LogicalExpressionInParenContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalExpressionInParen}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalExpressionInParen(PredicateParser.LogicalExpressionInParenContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code NotFunc}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterNotFunc(PredicateParser.NotFuncContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code NotFunc}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitNotFunc(PredicateParser.NotFuncContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalExpressionAnd}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalExpressionAnd(PredicateParser.LogicalExpressionAndContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalExpressionAnd}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalExpressionAnd(PredicateParser.LogicalExpressionAndContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalExpressionOr}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalExpressionOr(PredicateParser.LogicalExpressionOrContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalExpressionOr}
+	 * labeled alternative in {@link PredicateParser#logical_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalExpressionOr(PredicateParser.LogicalExpressionOrContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code ComparisonExpressionWithOperator}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterComparisonExpressionWithOperator(PredicateParser.ComparisonExpressionWithOperatorContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code ComparisonExpressionWithOperator}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitComparisonExpressionWithOperator(PredicateParser.ComparisonExpressionWithOperatorContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code InExpression}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterInExpression(PredicateParser.InExpressionContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code InExpression}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitInExpression(PredicateParser.InExpressionContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code NInExpression}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterNInExpression(PredicateParser.NInExpressionContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code NInExpression}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitNInExpression(PredicateParser.NInExpressionContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code ComparisonExpressionParens}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void enterComparisonExpressionParens(PredicateParser.ComparisonExpressionParensContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code ComparisonExpressionParens}
+	 * labeled alternative in {@link PredicateParser#comparison_expr}.
+	 * @param ctx the parse tree
+	 */
+	void exitComparisonExpressionParens(PredicateParser.ComparisonExpressionParensContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalConst}
+	 * labeled alternative in {@link PredicateParser#logical_entity}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalConst(PredicateParser.LogicalConstContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalConst}
+	 * labeled alternative in {@link PredicateParser#logical_entity}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalConst(PredicateParser.LogicalConstContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code ExistsFunc}
+	 * labeled alternative in {@link PredicateParser#logical_entity}.
+	 * @param ctx the parse tree
+	 */
+	void enterExistsFunc(PredicateParser.ExistsFuncContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code ExistsFunc}
+	 * labeled alternative in {@link PredicateParser#logical_entity}.
+	 * @param ctx the parse tree
+	 */
+	void exitExistsFunc(PredicateParser.ExistsFuncContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalFunc}
+	 * labeled alternative in {@link PredicateParser#logical_entity}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalFunc(PredicateParser.LogicalFuncContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalFunc}
+	 * labeled alternative in {@link PredicateParser#logical_entity}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalFunc(PredicateParser.LogicalFuncContext ctx);
+	/**
+	 * Enter a parse tree produced by {@link PredicateParser#list_entity}.
+	 * @param ctx the parse tree
+	 */
+	void enterList_entity(PredicateParser.List_entityContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link PredicateParser#list_entity}.
+	 * @param ctx the parse tree
+	 */
+	void exitList_entity(PredicateParser.List_entityContext ctx);
+	/**
+	 * Enter a parse tree produced by {@link PredicateParser#func_args}.
+	 * @param ctx the parse tree
+	 */
+	void enterFunc_args(PredicateParser.Func_argsContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link PredicateParser#func_args}.
+	 * @param ctx the parse tree
+	 */
+	void exitFunc_args(PredicateParser.Func_argsContext ctx);
+	/**
+	 * Enter a parse tree produced by {@link PredicateParser#op_list}.
+	 * @param ctx the parse tree
+	 */
+	void enterOp_list(PredicateParser.Op_listContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link PredicateParser#op_list}.
+	 * @param ctx the parse tree
+	 */
+	void exitOp_list(PredicateParser.Op_listContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code StringLiteral}
+	 * labeled alternative in {@link PredicateParser#identifier_operand}.
+	 * @param ctx the parse tree
+	 */
+	void enterStringLiteral(PredicateParser.StringLiteralContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code StringLiteral}
+	 * labeled alternative in {@link PredicateParser#identifier_operand}.
+	 * @param ctx the parse tree
+	 */
+	void exitStringLiteral(PredicateParser.StringLiteralContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalVariable}
+	 * labeled alternative in {@link PredicateParser#identifier_operand}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalVariable(PredicateParser.LogicalVariableContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalVariable}
+	 * labeled alternative in {@link PredicateParser#identifier_operand}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalVariable(PredicateParser.LogicalVariableContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code StringFunc}
+	 * labeled alternative in {@link PredicateParser#identifier_operand}.
+	 * @param ctx the parse tree
+	 */
+	void enterStringFunc(PredicateParser.StringFuncContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code StringFunc}
+	 * labeled alternative in {@link PredicateParser#identifier_operand}.
+	 * @param ctx the parse tree
+	 */
+	void exitStringFunc(PredicateParser.StringFuncContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code IdentifierOperand}
+	 * labeled alternative in {@link PredicateParser#comparison_operand}.
+	 * @param ctx the parse tree
+	 */
+	void enterIdentifierOperand(PredicateParser.IdentifierOperandContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code IdentifierOperand}
+	 * labeled alternative in {@link PredicateParser#comparison_operand}.
+	 * @param ctx the parse tree
+	 */
+	void exitIdentifierOperand(PredicateParser.IdentifierOperandContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code LogicalConstComparison}
+	 * labeled alternative in {@link PredicateParser#comparison_operand}.
+	 * @param ctx the parse tree
+	 */
+	void enterLogicalConstComparison(PredicateParser.LogicalConstComparisonContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code LogicalConstComparison}
+	 * labeled alternative in {@link PredicateParser#comparison_operand}.
+	 * @param ctx the parse tree
+	 */
+	void exitLogicalConstComparison(PredicateParser.LogicalConstComparisonContext ctx);
+	/**
+	 * Enter a parse tree produced by the {@code ComparisonOp}
+	 * labeled alternative in {@link PredicateParser#comp_operator}.
+	 * @param ctx the parse tree
+	 */
+	void enterComparisonOp(PredicateParser.ComparisonOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code ComparisonOp}
+	 * labeled alternative in {@link PredicateParser#comp_operator}.
+	 * @param ctx the parse tree
+	 */
+	void exitComparisonOp(PredicateParser.ComparisonOpContext ctx);
+}
\ No newline at end of file