http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/RestFunctions.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/RestFunctions.java
 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/RestFunctions.java
new file mode 100644
index 0000000..354322a
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/RestFunctions.java
@@ -0,0 +1,388 @@
+/**
+ * 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.stellar.dsl.functions;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+import org.apache.metron.stellar.common.utils.ConversionUtils;
+import org.apache.metron.stellar.common.utils.JSONUtils;
+import org.apache.metron.stellar.dsl.Context;
+import org.apache.metron.stellar.dsl.ParseException;
+import org.apache.metron.stellar.dsl.Stellar;
+import org.apache.metron.stellar.dsl.StellarFunction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.String.format;
+import static org.apache.metron.stellar.dsl.Context.Capabilities.GLOBAL_CONFIG;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.POOLING_DEFAULT_MAX_PER_RUOTE;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.POOLING_MAX_TOTAL;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.STELLAR_REST_SETTINGS;
+
+/**
+ * Defines functions that enable REST requests with proper result and error 
handling.  Depends on an
+ * Apache HttpComponents client being supplied as a Stellar HTTP_CLIENT 
capability.  Exposes various Http settings
+ * including authentication, proxy and timeouts through the global config with 
the option to override any settings
+ * through a config object supplied in the expression.
+ */
+public class RestFunctions {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  /**
+   * Get an argument from a list of arguments.
+   *
+   * @param index The index within the list of arguments.
+   * @param clazz The type expected.
+   * @param args All of the arguments.
+   * @param <T> The type of the argument expected.
+   */
+  public static <T> T getArg(int index, Class<T> clazz, List<Object> args) {
+
+    if(index >= args.size()) {
+      throw new IllegalArgumentException(format("Expected at least %d 
argument(s), found %d", index+1, args.size()));
+    }
+
+    return ConversionUtils.convert(args.get(index), clazz);
+  }
+
+  @Stellar(
+          namespace = "REST",
+          name = "GET",
+          description = "Performs a REST GET request and parses the JSON 
results into a map.",
+          params = {
+                  "url - URI to the REST service",
+                  "rest_config - Optional - Map (in curly braces) of 
name:value pairs, each overriding the global config parameter " +
+                          "of the same name. Default is the empty Map, meaning 
no overrides."
+          },
+          returns = "JSON results as a Map")
+  public static class RestGet implements StellarFunction {
+
+    /**
+     * Whether the function has been initialized.
+     */
+    private boolean initialized = false;
+
+    /**
+     * The CloseableHttpClient.
+     */
+    private CloseableHttpClient httpClient;
+
+    /**
+     * Executor used to impose a hard request timeout.
+     */
+    private ScheduledExecutorService scheduledExecutorService;
+
+    /**
+     * Initialize the function by creating a ScheduledExecutorService and 
looking up the CloseableHttpClient from the
+     * Stellar context.
+     * @param context
+     */
+    @Override
+    public void initialize(Context context) {
+      scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+      httpClient = getHttpClient(context);
+      initialized = true;
+    }
+
+    @Override
+    public boolean isInitialized() {
+      return initialized;
+    }
+
+    /**
+     * Apply the function.
+     * @param args The function arguments including uri and rest config.
+     * @param context Stellar context
+     */
+    @Override
+    public Object apply(List<Object> args, Context context) throws 
ParseException {
+      RestConfig restConfig = new RestConfig();
+      try {
+        URI uri = new URI(getArg(0, String.class, args));
+        restConfig = getRestConfig(args, getGlobalConfig(context));
+
+        HttpHost target = new HttpHost(uri.getHost(), uri.getPort(), 
uri.getScheme());
+        Optional<HttpHost> proxy = getProxy(restConfig);
+        HttpClientContext httpClientContext = getHttpClientContext(restConfig, 
target, proxy);
+
+        HttpGet httpGet = new HttpGet(uri);
+        httpGet.addHeader("Accept", "application/json");
+        httpGet.setConfig(getRequestConfig(restConfig, proxy));
+
+        return doGet(restConfig, httpGet, httpClientContext);
+      } catch (URISyntaxException e) {
+        throw new IllegalArgumentException(e.getMessage(), e);
+      } catch (IOException e) {
+        LOG.error(e.getMessage(), e);
+        return restConfig.getErrorValueOverride();
+      }
+    }
+
+    @Override
+    public void close() throws IOException {
+      if (httpClient != null) {
+        httpClient.close();
+      }
+      if (scheduledExecutorService != null) {
+        scheduledExecutorService.shutdown();
+      }
+    }
+
+    /**
+     * Retrieves the ClosableHttpClient from a pooling connection manager.
+     *
+     * @param context The execution context.
+     * @return A ClosableHttpClient.
+     */
+    protected CloseableHttpClient getHttpClient(Context context) {
+      RestConfig restConfig = getRestConfig(Collections.emptyList(), 
getGlobalConfig(context));
+
+      PoolingHttpClientConnectionManager cm = getConnectionManager(restConfig);
+
+      return HttpClients.custom()
+              .setConnectionManager(cm)
+              .build();
+    }
+
+    protected PoolingHttpClientConnectionManager 
getConnectionManager(RestConfig restConfig) {
+      PoolingHttpClientConnectionManager cm = new 
PoolingHttpClientConnectionManager();
+      if (restConfig.containsKey(POOLING_MAX_TOTAL)) {
+        cm.setMaxTotal(restConfig.getPoolingMaxTotal());
+      }
+      if (restConfig.containsKey(POOLING_DEFAULT_MAX_PER_RUOTE)) {
+        cm.setDefaultMaxPerRoute(restConfig.getPoolingDefaultMaxPerRoute());
+      }
+      return cm;
+    }
+
+    /**
+     * Only used for testing.
+     * @param httpClient
+     */
+    protected void setHttpClient(CloseableHttpClient httpClient) {
+      this.httpClient = httpClient;
+    }
+
+    /**
+     * Perform the HttpClient get and handle the results.  A configurable list 
of status codes are accepted and the
+     * response content (expected to be json) is parsed into a Map.  Values 
returned on errors and when response content
+     * is also configurable.  The rest config "timeout" setting is imposed in 
this method and will abort the get request
+     * if exceeded.
+     *
+     * @param restConfig
+     * @param httpGet
+     * @param httpClientContext
+     * @return
+     * @throws IOException
+     */
+    protected Object doGet(RestConfig restConfig, HttpGet httpGet, 
HttpClientContext httpClientContext) throws IOException {
+
+      // Schedule a command to abort the httpGet request if the timeout is 
exceeded
+      ScheduledFuture scheduledFuture = 
scheduledExecutorService.schedule(httpGet::abort, restConfig.getTimeout(), 
TimeUnit.MILLISECONDS);
+      CloseableHttpResponse response;
+      try {
+        response = httpClient.execute(httpGet, httpClientContext);
+      } catch(Exception e) {
+        // Report a timeout if the httpGet request was aborted.  Otherwise 
rethrow exception.
+        if (httpGet.isAborted()) {
+          throw new IOException(String.format("Total Stellar REST request time 
to %s exceeded the configured timeout of %d ms.", httpGet.getURI().toString(), 
restConfig.getTimeout()));
+        } else {
+          throw e;
+        }
+      }
+
+      // Cancel the future if the request finished within the timeout
+      if (!scheduledFuture.isDone()) {
+        scheduledFuture.cancel(true);
+      }
+      int statusCode = response.getStatusLine().getStatusCode();
+      if (restConfig.getResponseCodesAllowed().contains(statusCode)) {
+        HttpEntity httpEntity = response.getEntity();
+
+        // Parse the reponse if present, return the empty value override if not
+        if (httpEntity != null && httpEntity.getContentLength() > 0) {
+          String json = EntityUtils.toString(response.getEntity());
+          return JSONUtils.INSTANCE.load(json, JSONUtils.MAP_SUPPLIER);
+        }
+        return restConfig.getEmptyContentOverride();
+      } else {
+        throw new IOException(String.format("Stellar REST request to %s 
expected status code to be one of %s but " +
+                "failed with http status code %d: %s",
+                httpGet.getURI().toString(),
+                restConfig.getResponseCodesAllowed().toString(),
+                statusCode,
+                EntityUtils.toString(response.getEntity())));
+      }
+    }
+
+    private Map<String, Object> getGlobalConfig(Context context) {
+      Optional<Object> globalCapability = context.getCapability(GLOBAL_CONFIG, 
false);
+      return globalCapability.map(o -> (Map<String, Object>) 
o).orElseGet(HashMap::new);
+    }
+
+    /**
+     * Build the RestConfig object using the following order of precedence:
+     * <ul>
+     *   <li>rest config supplied as an expression parameter</li>
+     *   <li>rest config stored in the global config</li>
+     *   <li>default rest config</li>
+     * </ul>
+     * Only settings specified in the rest config will override lower priority 
config settings.
+     * @param args
+     * @param globalConfig
+     * @return
+     * @throws IOException
+     */
+    protected RestConfig getRestConfig(List<Object> args, Map<String, Object> 
globalConfig) {
+      Map<String, Object> globalRestConfig = (Map<String, Object>) 
globalConfig.get(STELLAR_REST_SETTINGS);
+      Map<String, Object> functionRestConfig = null;
+      if (args.size() > 1) {
+        functionRestConfig = getArg(1, Map.class, args);
+      }
+
+      // Add settings in order of precedence
+      RestConfig restConfig = new RestConfig();
+      if (globalRestConfig != null) {
+        restConfig.putAll(globalRestConfig);
+      }
+      if (functionRestConfig != null) {
+        restConfig.putAll(functionRestConfig);
+      }
+      return restConfig;
+    }
+
+    /**
+     * Returns the proxy HttpHost object if the proxy rest config settings are 
set.
+     * @param restConfig
+     * @return
+     */
+    protected Optional<HttpHost> getProxy(RestConfig restConfig) {
+      Optional<HttpHost> proxy = Optional.empty();
+      if (restConfig.getProxyHost() != null && restConfig.getProxyPort() != 
null) {
+        proxy = Optional.of(new HttpHost(restConfig.getProxyHost(), 
restConfig.getProxyPort(), "http"));
+      }
+      return proxy;
+    }
+
+    /**
+     * Builds the RequestConfig object by setting HttpClient settings defined 
in the rest config.
+     * @param restConfig
+     * @param proxy
+     * @return
+     */
+    protected RequestConfig getRequestConfig(RestConfig restConfig, 
Optional<HttpHost> proxy) {
+      RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
+      if (restConfig.getConnectTimeout() != null) {
+        requestConfigBuilder.setConnectTimeout(restConfig.getConnectTimeout());
+      }
+      if (restConfig.getConnectionRequestTimeout() != null) {
+        
requestConfigBuilder.setConnectionRequestTimeout(restConfig.getConnectionRequestTimeout());
+      }
+      if (restConfig.getSocketTimeout() != null) {
+        requestConfigBuilder.setSocketTimeout(restConfig.getSocketTimeout());
+      }
+
+      proxy.ifPresent(requestConfigBuilder::setProxy);
+      return requestConfigBuilder.build();
+    }
+
+    /**
+     * Builds the HttpClientContext object by setting the basic auth and/or 
proxy basic auth credentials when the
+     * necessary rest config settings are configured.  Passwords are stored in 
HDFS.
+     * @param restConfig
+     * @param target
+     * @param proxy
+     * @return
+     * @throws IOException
+     */
+    protected HttpClientContext getHttpClientContext(RestConfig restConfig, 
HttpHost target, Optional<HttpHost> proxy) throws IOException {
+      HttpClientContext httpClientContext = HttpClientContext.create();
+      boolean credentialsAdded = false;
+      CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+
+      // Add the basic auth credentials if the rest config settings are present
+      if (restConfig.getBasicAuthUser() != null && 
restConfig.getBasicAuthPasswordPath() != null) {
+        String password = new String(readBytes(new 
Path(restConfig.getBasicAuthPasswordPath())), StandardCharsets.UTF_8);
+        credentialsProvider.setCredentials(
+                new AuthScope(target),
+                new UsernamePasswordCredentials(restConfig.getBasicAuthUser(), 
password));
+        credentialsAdded = true;
+      }
+
+      // Add the proxy basic auth credentials if the rest config settings are 
present
+      if (proxy.isPresent() && restConfig.getProxyBasicAuthUser() != null &&
+              restConfig.getProxyBasicAuthPasswordPath() != null) {
+        String password = new String(readBytes(new 
Path(restConfig.getProxyBasicAuthPasswordPath())), StandardCharsets.UTF_8);
+        credentialsProvider.setCredentials(
+                new AuthScope(proxy.get()),
+                new 
UsernamePasswordCredentials(restConfig.getProxyBasicAuthUser(), password));
+        credentialsAdded = true;
+      }
+      if (credentialsAdded) {
+        httpClientContext.setCredentialsProvider(credentialsProvider);
+      }
+      return httpClientContext;
+    }
+
+    /**
+     * Read bytes from a HDFS path.
+     * @param inPath
+     * @return
+     * @throws IOException
+     */
+    private byte[] readBytes(Path inPath) throws IOException {
+      FileSystem fs = FileSystem.get(inPath.toUri(), new Configuration());
+      try (FSDataInputStream inputStream = fs.open(inPath)) {
+        return IOUtils.toByteArray(inputStream);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolver.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolver.java
 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolver.java
index aeed9d9..38a32d1 100644
--- 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolver.java
+++ 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolver.java
@@ -23,6 +23,7 @@ import static java.lang.String.format;
 import com.google.common.base.Joiner;
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
+import java.io.IOException;
 import java.io.Serializable;
 import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
@@ -58,9 +59,15 @@ public abstract class BaseFunctionResolver implements 
FunctionResolver, Serializ
    */
   protected Context context;
 
+  /**
+   * Indicates if closed has been called on this resolver.
+   */
+  private boolean closed;
+
   public BaseFunctionResolver() {
     // memoize provides lazy initialization and thread-safety (the ugly cast 
is necessary for serialization)
     functions = Suppliers.memoize((Supplier<Map<String, StellarFunctionInfo>> 
& Serializable) this::resolveFunctions);
+    closed = false;
   }
 
   /**
@@ -95,6 +102,43 @@ public abstract class BaseFunctionResolver implements 
FunctionResolver, Serializ
   }
 
   /**
+   * Makes an attempt to close all Stellar functions. Calling close multiple 
times has no effect.
+   * @throws IOException Catches all exceptions and summarizes them.
+   */
+  @Override
+  public void close() throws IOException {
+    if (!closed) {
+      LOG.info("Calling close() on Stellar functions.");
+      Map<String, Throwable> errors = new HashMap<>();
+      for (StellarFunctionInfo info : getFunctionInfo()) {
+        try {
+          info.getFunction().close();
+        } catch (Throwable t) {
+          errors.put(info.getName(), t);
+        }
+      }
+      if (!errors.isEmpty()) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Unable to close Stellar functions:");
+        for (Map.Entry<String, Throwable> e : errors.entrySet()) {
+          Throwable throwable = e.getValue();
+          String eText = String
+              .format("Exception - Function: %s; Message: %s; Cause: %s", 
e.getKey(),
+                  throwable.getMessage(),
+                  throwable.getCause());
+          sb.append(System.lineSeparator());
+          sb.append(eText);
+        }
+        closed = true;
+        throw new IOException(sb.toString());
+      }
+      closed = true;
+    } else {
+      LOG.info("close() already called on Stellar functions - skipping.");
+    }
+  }
+
+  /**
    * Resolves a function by name.
    * @param functionName The name of the function to resolve.
    * @return The executable StellarFunction.

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/FunctionResolver.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/FunctionResolver.java
 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/FunctionResolver.java
index 5acb42c..4047586 100644
--- 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/FunctionResolver.java
+++ 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/resolver/FunctionResolver.java
@@ -17,16 +17,17 @@
  */
 package org.apache.metron.stellar.dsl.functions.resolver;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.function.Function;
 import org.apache.metron.stellar.dsl.Context;
 import org.apache.metron.stellar.dsl.StellarFunction;
 import org.apache.metron.stellar.dsl.StellarFunctionInfo;
 
-import java.util.function.Function;
-
 /**
  * Responsible for function resolution in Stellar.
  */
-public interface FunctionResolver extends Function<String, StellarFunction> {
+public interface FunctionResolver extends Function<String, StellarFunction>, 
Closeable {
 
   /**
    * Provides metadata about each Stellar function that is resolvable.
@@ -43,4 +44,11 @@ public interface FunctionResolver extends Function<String, 
StellarFunction> {
    * @param context Context used to initialize.
    */
   void initialize(Context context);
+
+  /**
+   * Perform any cleanup necessary for the loaded Stellar functions.
+   */
+  @Override
+  default void close() throws IOException {}
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/BasicStellarTest.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/BasicStellarTest.java
 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/BasicStellarTest.java
index dec05a8..79f97bc 100644
--- 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/BasicStellarTest.java
+++ 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/BasicStellarTest.java
@@ -18,9 +18,20 @@
 
 package org.apache.metron.stellar.dsl.functions;
 
+import static org.apache.metron.stellar.common.utils.StellarProcessorUtils.run;
+import static 
org.apache.metron.stellar.common.utils.StellarProcessorUtils.runPredicate;
+import static 
org.apache.metron.stellar.common.utils.StellarProcessorUtils.validate;
+
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.metron.stellar.common.StellarProcessor;
 import org.apache.metron.stellar.dsl.Context;
@@ -37,12 +48,6 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import java.util.*;
-
-import static org.apache.metron.stellar.common.utils.StellarProcessorUtils.run;
-import static 
org.apache.metron.stellar.common.utils.StellarProcessorUtils.validate;
-import static 
org.apache.metron.stellar.common.utils.StellarProcessorUtils.runPredicate;
-
 @SuppressWarnings("ALL")
 public class BasicStellarTest {
 
@@ -70,6 +75,7 @@ public class BasicStellarTest {
     public boolean isInitialized() {
       return true;
     }
+
   }
 
   @Stellar(
@@ -96,6 +102,7 @@ public class BasicStellarTest {
     public boolean isInitialized() {
       return true;
     }
+
   }
 
   @Test
@@ -1000,4 +1007,5 @@ public class BasicStellarTest {
     checkFalsey("{}");
     checkFalsey("LIST_ADD([])");
   }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/DateFunctionsTest.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/DateFunctionsTest.java
 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/DateFunctionsTest.java
index 1f1f4f4..48b2995 100644
--- 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/DateFunctionsTest.java
+++ 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/DateFunctionsTest.java
@@ -28,9 +28,12 @@ import org.apache.metron.stellar.dsl.StellarFunctions;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.TimeZone;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -225,4 +228,47 @@ public class DateFunctionsTest {
   public void testDayOfYearNull() {
     Object result = run("DAY_OF_YEAR(nada)");
   }
+
+  @Test
+  public void testDateFormat() {
+    Object result = run("DATE_FORMAT('EEE MMM dd yyyy hh:mm:ss zzz', epoch, 
'EST')");
+    assertEquals("Thu Aug 25 2016 08:27:10 EST", result);
+  }
+
+  /**
+   * Test that the String returned is formatted as specified.
+   * LocalDate.parse will throw if it is not.
+   * @throws Exception
+   */
+  @Test
+  public void testDateFormatDefault() throws Exception {
+    Object result = run("DATE_FORMAT('EEE MMM dd yyyy hh:mm:ss zzzz')");
+    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd yyyy 
hh:mm:ss zzzz");
+    LocalDate.parse(result.toString(), formatter);
+  }
+
+  @Test
+  public void testDateFormatNow() {
+    Object result = run("DATE_FORMAT('EEE MMM dd yyyy hh:mm:ss zzz', 'GMT')");
+    assertTrue(result.toString().endsWith("GMT"));
+  }
+
+  @Test
+  public void testDateFormatDefaultTimezone() {
+    Object result = run("DATE_FORMAT('EEE MMM dd yyyy hh:mm:ss zzzz', epoch)");
+    
assertTrue(result.toString().endsWith(TimeZone.getDefault().getDisplayName(true,
 1)));
+  }
+
+  /**
+   * If refer to variable that does not exist, expect ParseException.
+   */
+  @Test(expected = ParseException.class)
+  public void testDateFormatNull() {
+    Object result = run("DATE_FORMAT('EEE MMM dd yyyy hh:mm:ss zzz', nada, 
'EST')");
+  }
+
+  @Test(expected = ParseException.class)
+  public void testDateFormatInvalid() {
+    Object result = run("DATE_FORMAT('INVALID DATE FORMAT', epoch, 'EST')");
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/RestFunctionsTest.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/RestFunctionsTest.java
 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/RestFunctionsTest.java
new file mode 100644
index 0000000..4b912ef
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/RestFunctionsTest.java
@@ -0,0 +1,601 @@
+/**
+ * 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.stellar.dsl.functions;
+
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.metron.stellar.dsl.Context;
+import org.apache.metron.stellar.dsl.ParseException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockserver.client.server.MockServerClient;
+import org.mockserver.junit.MockServerRule;
+import org.mockserver.junit.ProxyRule;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.apache.metron.stellar.common.utils.StellarProcessorUtils.run;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.BASIC_AUTH_PASSWORD_PATH;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.BASIC_AUTH_USER;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.CONNECTION_REQUEST_TIMEOUT;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.CONNECT_TIMEOUT;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.POOLING_DEFAULT_MAX_PER_RUOTE;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.POOLING_MAX_TOTAL;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.PROXY_BASIC_AUTH_PASSWORD_PATH;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.PROXY_BASIC_AUTH_USER;
+import static org.apache.metron.stellar.dsl.functions.RestConfig.PROXY_HOST;
+import static org.apache.metron.stellar.dsl.functions.RestConfig.PROXY_PORT;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.SOCKET_TIMEOUT;
+import static 
org.apache.metron.stellar.dsl.functions.RestConfig.STELLAR_REST_SETTINGS;
+import static org.apache.metron.stellar.dsl.functions.RestConfig.TIMEOUT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+
+/**
+ * Tests the RestFunctions class.
+ */
+public class RestFunctionsTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Rule
+  public MockServerRule mockServerRule = new MockServerRule(this);
+
+  @Rule
+  public ProxyRule proxyRule = new ProxyRule(1080, this);
+
+  private MockServerClient mockServerClient;
+  private String getUri;
+  private Context context;
+
+  private String basicAuthPasswordPath = "./target/basicAuth.txt";
+  private String basicAuthPassword = "password";
+  private String proxyBasicAuthPasswordPath = "./target/proxyBasicAuth.txt";
+  private String proxyAuthPassword = "proxyPassword";
+
+  @Before
+  public void setup() throws Exception {
+    context = new Context.Builder()
+            .with(Context.Capabilities.GLOBAL_CONFIG, HashMap::new)
+            .build();
+
+    // Store the passwords in the local file system
+    FileUtils.writeStringToFile(new File(basicAuthPasswordPath), 
basicAuthPassword, StandardCharsets.UTF_8);
+    FileUtils.writeStringToFile(new File(proxyBasicAuthPasswordPath), 
proxyAuthPassword, StandardCharsets.UTF_8);
+
+    // By default, the mock server expects a GET request with the path set to 
/get
+    getUri = String.format("http://localhost:%d/get";, 
mockServerRule.getPort());
+    mockServerClient.when(
+            request()
+                    .withMethod("GET")
+                    .withPath("/get"))
+            .respond(response()
+                    .withBody("{\"get\":\"success\"}"));
+  }
+
+  /**
+   * The REST_GET function should perform a get request and parse the results.
+   */
+  @Test
+  public void restGetShouldSucceed() throws Exception {
+    Map<String, Object> actual = (Map<String, Object>) 
run(String.format("REST_GET('%s')", getUri), context);
+
+    assertEquals(1, actual.size());
+    assertEquals("success", actual.get("get"));
+  }
+
+  /**
+   * The REST_GET function should perform a get request using a proxy and 
parse the results.
+   */
+  @Test
+  public void restGetShouldSucceedWithProxy() {
+    mockServerClient.when(
+            request()
+                    .withMethod("GET")
+                    .withPath("/get"))
+            .respond(response()
+                    .withBody("{\"proxyGet\":\"success\"}"));
+
+    context.addCapability(Context.Capabilities.GLOBAL_CONFIG, () -> new 
HashMap<String, Object>() {{
+      put(PROXY_HOST, "localhost");
+      put(PROXY_PORT, proxyRule.getHttpPort());
+    }});
+
+    Map<String, Object> actual = (Map<String, Object>) 
run(String.format("REST_GET('%s')", getUri), context);
+
+    assertEquals(1, actual.size());
+    assertEquals("success", actual.get("proxyGet"));
+  }
+
+  /**
+   * The REST_GET function should handle an error status code and return null 
by default.
+   */
+  @Test
+  public void restGetShouldHandleErrorStatusCode() {
+      mockServerClient.when(
+              request()
+                      .withMethod("GET")
+                      .withPath("/get"))
+              .respond(response()
+                      .withStatusCode(403));
+
+      assertNull(run(String.format("REST_GET('%s')", getUri), context));
+  }
+
+  /**
+   * {
+   *   "response.codes.allowed": [200,404],
+   *   "empty.content.override": {}
+   * }
+   */
+  @Multiline
+  private String emptyContentOverride;
+
+  /**
+   * The REST_GET function should return the empty content override setting 
when status is allowed and content is empty.
+   */
+  @Test
+  public void restGetShouldReturnEmptyContentOverride() {
+      mockServerClient.when(
+              request()
+                      .withMethod("GET")
+                      .withPath("/get"))
+              .respond(response()
+                      .withStatusCode(404));
+
+    assertEquals(new HashMap<>(), run(String.format("REST_GET('%s', %s)", 
getUri, emptyContentOverride), context));
+  }
+
+  /**
+   * {
+   *   "error.value.override": "error message"
+   * }
+   */
+  @Multiline
+  private String errorValueOverride;
+
+  /**
+   * The REST_GET function should return the error value override setting on 
error.
+   */
+  @Test
+  public void restGetShouldReturnErrorValueOverride() {
+    mockServerClient.when(
+            request()
+                    .withMethod("GET")
+                    .withPath("/get"))
+            .respond(response()
+                    .withStatusCode(500));
+
+    Object result = run(String.format("REST_GET('%s', %s)", getUri, 
errorValueOverride), context);
+    assertEquals("error message" , result);
+  }
+
+  /**
+   * The REST_GET function should return a proxy HttpHost if the correct 
settings are present.
+   */
+  @Test
+  public void restGetShouldGetProxy() {
+    RestFunctions.RestGet restGet = new RestFunctions.RestGet();
+
+    {
+      RestConfig restConfig = new RestConfig();
+      Optional<HttpHost> actual = restGet.getProxy(restConfig);
+
+      assertEquals(Optional.empty(), actual);
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(PROXY_HOST, "localhost");
+      Optional<HttpHost> actual = restGet.getProxy(restConfig);
+
+      assertEquals(Optional.empty(), actual);
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(PROXY_PORT, proxyRule.getHttpPort());
+      Optional<HttpHost> actual = restGet.getProxy(restConfig);
+
+      assertEquals(Optional.empty(), actual);
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(PROXY_HOST, "localhost");
+      restConfig.put(PROXY_PORT, proxyRule.getHttpPort());
+      Optional<HttpHost> actual = restGet.getProxy(restConfig);
+
+      assertEquals(new HttpHost("localhost", proxyRule.getHttpPort()), 
actual.get());
+    }
+  }
+
+  /**
+   * The REST_GET function should return settings in the correct order of 
precedence.
+   * @throws Exception
+   */
+  @Test
+  public void restGetShouldGetRestConfig() throws Exception {
+    RestFunctions.RestGet restGet = new RestFunctions.RestGet();
+
+    {
+      // Test for default timeout
+      RestConfig restConfig = 
restGet.getRestConfig(Collections.singletonList("uri"), new HashMap<>());
+
+      assertEquals(2, restConfig.size());
+      assertEquals(1000, restConfig.getTimeout().intValue());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertNull(restConfig.getBasicAuthUser());
+    }
+
+    Map<String, Object> globalRestConfig = new HashMap<String, Object>() {{
+      put(STELLAR_REST_SETTINGS, new HashMap<String, Object>() {{
+        put(SOCKET_TIMEOUT, 2000);
+        put(BASIC_AUTH_USER, "globalUser");
+        put(PROXY_HOST, "globalHost");
+      }});
+    }};
+
+    // Global config settings should take effect
+    {
+      RestConfig restConfig = 
restGet.getRestConfig(Collections.singletonList("uri"), globalRestConfig);
+
+      assertEquals(5, restConfig.size());
+      assertEquals(1000, restConfig.getTimeout().intValue());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertEquals(2000, restConfig.getSocketTimeout().intValue());
+      assertEquals("globalUser", restConfig.getBasicAuthUser());
+      assertEquals("globalHost", restConfig.getProxyHost());
+    }
+
+    Map<String, Object> functionRestConfig = new HashMap<String, Object>() {{
+      put(SOCKET_TIMEOUT, 1);
+      put(BASIC_AUTH_USER, "functionUser");
+      put(TIMEOUT, 100);
+    }};
+
+
+    // Function call settings should override global settings
+    {
+      RestConfig restConfig = restGet.getRestConfig(Arrays.asList("uri", 
functionRestConfig), globalRestConfig);
+
+      assertEquals(5, restConfig.size());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertEquals(100, restConfig.getTimeout().intValue());
+      assertEquals(1, restConfig.getSocketTimeout().intValue());
+      assertEquals("functionUser", restConfig.getBasicAuthUser());
+      assertEquals("globalHost", restConfig.getProxyHost());
+    }
+
+    functionRestConfig = new HashMap<String, Object>() {{
+      put(BASIC_AUTH_USER, "functionUser");
+      put(TIMEOUT, 100);
+    }};
+
+    // New function call settings should take effect with global settings 
staying the same
+    {
+      RestConfig restConfig = restGet.getRestConfig(Arrays.asList("uri", 
functionRestConfig), globalRestConfig);
+
+      assertEquals(5, restConfig.size());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertEquals(100, restConfig.getTimeout().intValue());
+      assertEquals(2000, restConfig.getSocketTimeout().intValue());
+      assertEquals("functionUser", restConfig.getBasicAuthUser());
+      assertEquals("globalHost", restConfig.getProxyHost());
+    }
+
+    globalRestConfig = new HashMap<String, Object>() {{
+      put(STELLAR_REST_SETTINGS, new HashMap<String, Object>() {{
+        put(SOCKET_TIMEOUT, 2000);
+        put(BASIC_AUTH_USER, "globalUser");
+      }});
+    }};
+
+    // New global settings should take effect with function call settings 
staying the same
+    {
+      RestConfig restConfig = restGet.getRestConfig(Arrays.asList("uri", 
functionRestConfig), globalRestConfig);
+
+      assertEquals(4, restConfig.size());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertEquals(100, restConfig.getTimeout().intValue());
+      assertEquals(2000, restConfig.getSocketTimeout().intValue());
+      assertEquals("functionUser", restConfig.getBasicAuthUser());
+    }
+
+    // Should fall back to global settings on missing function call config
+    {
+      RestConfig restConfig = 
restGet.getRestConfig(Collections.singletonList("uri"), globalRestConfig);
+
+      assertEquals(4, restConfig.size());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertEquals(1000, restConfig.getTimeout().intValue());
+      assertEquals(2000, restConfig.getSocketTimeout().intValue());
+      assertEquals("globalUser", restConfig.getBasicAuthUser());
+    }
+
+    // Should fall back to default settings on missing global settings
+    {
+      RestConfig restConfig = 
restGet.getRestConfig(Collections.singletonList("uri"), new HashMap<>());
+
+      assertEquals(2, restConfig.size());
+      assertEquals(Collections.singletonList(200), 
restConfig.getResponseCodesAllowed());
+      assertEquals(1000, restConfig.getTimeout().intValue());
+    }
+
+  }
+
+  /**
+   * The REST_GET function should properly set the HttpClient timeout settings 
and proxy
+   */
+  @Test
+  public void restGetShouldGetRequestConfig() {
+    RestFunctions.RestGet restGet = new RestFunctions.RestGet();
+
+    {
+      RequestConfig actual = restGet.getRequestConfig(new RestConfig(), 
Optional.empty());
+      RequestConfig expected = RequestConfig.custom().build();
+
+      assertEquals(expected.getConnectTimeout(), actual.getConnectTimeout());
+      assertEquals(expected.getConnectionRequestTimeout(), 
actual.getConnectionRequestTimeout());
+      assertEquals(expected.getSocketTimeout(), actual.getSocketTimeout());
+      assertEquals(expected.getProxy(), actual.getProxy());
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(CONNECT_TIMEOUT, 1);
+      restConfig.put(CONNECTION_REQUEST_TIMEOUT, 2);
+      restConfig.put(SOCKET_TIMEOUT, 3);
+      HttpHost proxy = new HttpHost("localhost", proxyRule.getHttpPort());
+      Optional<HttpHost> proxyOptional = Optional.of(proxy);
+
+      RequestConfig actual = restGet.getRequestConfig(restConfig, 
proxyOptional);
+      RequestConfig expected = RequestConfig.custom()
+              .setConnectTimeout(1)
+              .setConnectionRequestTimeout(2)
+              .setSocketTimeout(3)
+              .setProxy(proxy)
+              .build();
+
+      assertEquals(expected.getConnectTimeout(), actual.getConnectTimeout());
+      assertEquals(expected.getConnectionRequestTimeout(), 
actual.getConnectionRequestTimeout());
+      assertEquals(expected.getSocketTimeout(), actual.getSocketTimeout());
+      assertEquals(expected.getProxy(), actual.getProxy());
+    }
+
+  }
+
+  /**
+   * The REST_GET function should set the proper credentials in the 
HttpClientContext.
+   * @throws Exception
+   */
+  @Test
+  public void restGetShouldGetHttpClientContext() throws Exception {
+    RestFunctions.RestGet restGet = new RestFunctions.RestGet();
+    HttpHost target = new HttpHost("localhost", mockServerRule.getPort());
+    HttpHost proxy = new HttpHost("localhost", proxyRule.getHttpPort());
+
+    {
+      RestConfig restConfig = new RestConfig();
+      HttpClientContext actual = restGet.getHttpClientContext(restConfig, 
target, Optional.empty());
+
+      assertNull(actual.getCredentialsProvider());
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(BASIC_AUTH_USER, "user");
+      restConfig.put(BASIC_AUTH_PASSWORD_PATH, basicAuthPasswordPath);
+
+      HttpClientContext actual = restGet.getHttpClientContext(restConfig, 
target, Optional.empty());
+      HttpClientContext expected = HttpClientContext.create();
+      CredentialsProvider expectedCredentialsProvider = new 
BasicCredentialsProvider();
+      expectedCredentialsProvider.setCredentials(
+              new AuthScope(target),
+              new UsernamePasswordCredentials(restConfig.getBasicAuthUser(), 
basicAuthPassword));
+      expected.setCredentialsProvider(expectedCredentialsProvider);
+
+      assertEquals(expected.getCredentialsProvider().getCredentials(new 
AuthScope(target)),
+              actual.getCredentialsProvider().getCredentials(new 
AuthScope(target)));
+      assertEquals(expected.getCredentialsProvider().getCredentials(new 
AuthScope(proxy)),
+              actual.getCredentialsProvider().getCredentials(new 
AuthScope(proxy)));
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(PROXY_BASIC_AUTH_USER, "proxyUser");
+      restConfig.put(PROXY_BASIC_AUTH_PASSWORD_PATH, 
proxyBasicAuthPasswordPath);
+
+      HttpClientContext actual = restGet.getHttpClientContext(restConfig, 
target, Optional.of(proxy));
+      HttpClientContext expected = HttpClientContext.create();
+      CredentialsProvider expectedCredentialsProvider = new 
BasicCredentialsProvider();
+      expectedCredentialsProvider.setCredentials(
+              new AuthScope(proxy),
+              new 
UsernamePasswordCredentials(restConfig.getProxyBasicAuthUser(), 
proxyAuthPassword));
+      expected.setCredentialsProvider(expectedCredentialsProvider);
+
+      assertEquals(expected.getCredentialsProvider().getCredentials(new 
AuthScope(target)),
+              actual.getCredentialsProvider().getCredentials(new 
AuthScope(target)));
+      assertEquals(expected.getCredentialsProvider().getCredentials(new 
AuthScope(proxy)),
+              actual.getCredentialsProvider().getCredentials(new 
AuthScope(proxy)));
+    }
+
+    {
+      RestConfig restConfig = new RestConfig();
+      restConfig.put(BASIC_AUTH_USER, "user");
+      restConfig.put(BASIC_AUTH_PASSWORD_PATH, basicAuthPasswordPath);
+      restConfig.put(PROXY_BASIC_AUTH_USER, "proxyUser");
+      restConfig.put(PROXY_BASIC_AUTH_PASSWORD_PATH, 
proxyBasicAuthPasswordPath);
+
+      HttpClientContext actual = restGet.getHttpClientContext(restConfig, 
target, Optional.of(proxy));
+      HttpClientContext expected = HttpClientContext.create();
+      CredentialsProvider expectedCredentialsProvider = new 
BasicCredentialsProvider();
+      expectedCredentialsProvider.setCredentials(
+              new AuthScope(target),
+              new UsernamePasswordCredentials(restConfig.getBasicAuthUser(), 
basicAuthPassword));
+      expectedCredentialsProvider.setCredentials(
+              new AuthScope(proxy),
+              new 
UsernamePasswordCredentials(restConfig.getProxyBasicAuthUser(), 
proxyAuthPassword));
+      expected.setCredentialsProvider(expectedCredentialsProvider);
+
+      assertEquals(expected.getCredentialsProvider().getCredentials(new 
AuthScope(target)),
+              actual.getCredentialsProvider().getCredentials(new 
AuthScope(target)));
+      assertEquals(expected.getCredentialsProvider().getCredentials(new 
AuthScope(proxy)),
+              actual.getCredentialsProvider().getCredentials(new 
AuthScope(proxy)));
+    }
+  }
+
+  /**
+   * The REST_GET function should timeout and return null.
+   */
+  @Test
+  public void restGetShouldTimeout() {
+    String uri = String.format("http://localhost:%d/get";, 
mockServerRule.getPort());
+
+    mockServerClient.when(
+            request()
+                    .withMethod("GET")
+                    .withPath("/get"))
+            .respond(response()
+                    .withBody("{\"get\":\"success\"}"));
+
+    Map<String, Object> globalConfig = new HashMap<String, Object>() {{
+      put(STELLAR_REST_SETTINGS, new HashMap<String, Object>() {{
+        put(TIMEOUT, 1);
+      }});
+    }};
+
+    context.addCapability(Context.Capabilities.GLOBAL_CONFIG, () -> 
globalConfig);
+
+    Map<String, Object> actual = (Map<String, Object>) 
run(String.format("REST_GET('%s')", uri), context);
+    assertNull(actual);
+  }
+
+  /**
+   * {
+   * "timeout": 1
+   * }
+   */
+  @Multiline
+  private String timeoutConfig;
+
+  /**
+   * The REST_GET function should honor the function supplied timeout setting.
+   */
+  @Test
+  public void restGetShouldTimeoutWithSuppliedTimeout() {
+    String expression = String.format("REST_GET('%s', %s)", getUri, 
timeoutConfig);
+    Map<String, Object> actual = (Map<String, Object>) run(expression, 
context);
+    assertNull(actual);
+  }
+
+  /**
+   * The REST_GET function should throw an exception on a malformed uri.
+   * @throws IllegalArgumentException
+   * @throws IOException
+   */
+  @Test
+  public void restGetShouldHandleURISyntaxException() throws 
IllegalArgumentException, IOException {
+    thrown.expect(ParseException.class);
+    thrown.expectMessage("Unable to parse REST_GET('some invalid uri'): Unable 
to parse: REST_GET('some invalid uri') due to: Illegal character in path at 
index 4: some invalid uri");
+
+    run("REST_GET('some invalid uri')", context);
+  }
+
+  /**
+   * The REST_GET function should handle IOExceptions and return null.
+   * @throws IllegalArgumentException
+   * @throws IOException
+   */
+  @Test
+  public void restGetShouldHandleIOException() throws 
IllegalArgumentException, IOException {
+    RestFunctions.RestGet restGet = spy(RestFunctions.RestGet.class);
+    doThrow(new IOException("io 
exception")).when(restGet).doGet(any(RestConfig.class), any(HttpGet.class), 
any(HttpClientContext.class));
+
+    Object result = restGet.apply(Collections.singletonList(getUri), context);
+    Assert.assertNull(result);
+  }
+
+  /**
+   * The REST_GET function should throw an exception when the required uri 
parameter is missing.
+   */
+  @Test
+  public void restGetShouldThrownExceptionOnMissingParameter() {
+    thrown.expect(ParseException.class);
+    thrown.expectMessage("Unable to parse REST_GET(): Unable to parse: 
REST_GET() due to: Expected at least 1 argument(s), found 0");
+
+    run("REST_GET()", context);
+  }
+
+  @Test
+  public void restGetShouldGetPoolingConnectionManager() {
+    RestFunctions.RestGet restGet = new RestFunctions.RestGet();
+
+    RestConfig restConfig = new RestConfig();
+    restConfig.put(POOLING_MAX_TOTAL, 5);
+    restConfig.put(POOLING_DEFAULT_MAX_PER_RUOTE, 2);
+
+    PoolingHttpClientConnectionManager cm = 
restGet.getConnectionManager(restConfig);
+
+    assertEquals(5, cm.getMaxTotal());
+    assertEquals(2, cm.getDefaultMaxPerRoute());
+  }
+
+  @Test
+  public void restGetShouldCloseHttpClient() throws Exception {
+    RestFunctions.RestGet restGet = new RestFunctions.RestGet();
+    CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
+
+    restGet.setHttpClient(httpClient);
+    restGet.close();
+
+    verify(httpClient, times(1)).close();
+    verifyNoMoreInteractions(httpClient);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolverTest.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolverTest.java
 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolverTest.java
new file mode 100644
index 0000000..47cbda3
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/resolver/BaseFunctionResolverTest.java
@@ -0,0 +1,169 @@
+/**
+ * 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.stellar.dsl.functions.resolver;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.metron.stellar.dsl.BaseStellarFunction;
+import org.apache.metron.stellar.dsl.Stellar;
+import org.apache.metron.stellar.dsl.StellarFunction;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class BaseFunctionResolverTest {
+
+  public static class TestResolver extends BaseFunctionResolver {
+
+    Set<Class<? extends StellarFunction>> classesToResolve = new HashSet<>();
+
+    @Override
+    public Set<Class<? extends StellarFunction>> resolvables() {
+      return classesToResolve;
+    }
+
+    /**
+     * Will attempt to resolve any Stellar functions defined within the 
specified class.
+     * @param clazz The class which may contain a Stellar function.
+     */
+    public TestResolver withClass(Class<? extends StellarFunction> clazz) {
+      this.classesToResolve.add(clazz);
+      return this;
+    }
+  }
+
+  /**
+   * Often imitated, never duplicated.
+   */
+  @Stellar(namespace = "namespace", name = "afunction", description = 
"description", returns = "returns", params = {
+      "param1"})
+  private static class IAmAFunction extends BaseStellarFunction {
+
+    public static int closeCallCount;
+    public static boolean throwException = false; // init here bc of 
reflection in resolver.
+
+    public IAmAFunction() {
+      closeCallCount = 0;
+    }
+
+    @Override
+    public Object apply(List<Object> args) {
+      return null;
+    }
+
+    @Override
+    public void close() throws IOException {
+      closeCallCount++;
+      if (throwException) {
+        Throwable cause = new Throwable("Some nasty nasty cause.");
+        throw new IOException("Bad things happened", cause);
+      }
+    }
+  }
+
+  /**
+   * Scratch that. I was wrong.
+   */
+  @Stellar(namespace = "namespace", name = "anotherfunction", description = 
"description", returns = "returns", params = {
+      "param1"})
+  private static class IAmAnotherFunction extends BaseStellarFunction {
+
+    public static int closeCallCount;
+    public static boolean throwException = false; // init here bc of 
reflection in resolver.
+
+    public IAmAnotherFunction() {
+      closeCallCount = 0;
+    }
+
+    @Override
+    public Object apply(List<Object> args) {
+      return null;
+    }
+
+    @Override
+    public void close() throws IOException {
+      closeCallCount++;
+      if (throwException) {
+        throw new NullPointerException("A most annoying exception.");
+      }
+    }
+  }
+
+  private TestResolver resolver;
+
+  @Before
+  public void setup() {
+    resolver = new TestResolver();
+    IAmAFunction.throwException = false;
+    IAmAnotherFunction.throwException = false;
+  }
+
+  @Test
+  public void close_calls_all_loaded_function_close_methods() throws 
IOException {
+    resolver.withClass(IAmAFunction.class);
+    resolver.withClass(IAmAnotherFunction.class);
+    resolver.close();
+    assertThat(IAmAFunction.closeCallCount, equalTo(1));
+    assertThat(IAmAnotherFunction.closeCallCount, equalTo(1));
+  }
+
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  @Test
+  public void 
close_collects_all_exceptions_thrown_on_loaded_function_close_methods()
+      throws IOException {
+    IAmAFunction.throwException = true;
+    IAmAnotherFunction.throwException = true;
+    resolver.withClass(IAmAFunction.class);
+    resolver.withClass(IAmAnotherFunction.class);
+    exception.expect(IOException.class);
+    resolver.close();
+  }
+
+  @Test
+  public void close_only_throws_exceptions_on_first_invocation()
+      throws IOException {
+    IAmAFunction.throwException = true;
+    IAmAnotherFunction.throwException = true;
+    resolver.withClass(IAmAFunction.class);
+    resolver.withClass(IAmAnotherFunction.class);
+    try {
+      resolver.close();
+      Assert.fail("Should have thrown an exception.");
+    } catch (IOException e) {
+      // intentionally empty
+    }
+    assertThat(IAmAFunction.closeCallCount, equalTo(1));
+    assertThat(IAmAnotherFunction.closeCallCount, equalTo(1));
+    // should not throw exceptions or call any function's close again.
+    resolver.close();
+    resolver.close();
+    resolver.close();
+    assertThat(IAmAFunction.closeCallCount, equalTo(1));
+    assertThat(IAmAnotherFunction.closeCallCount, equalTo(1));
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8fe1786..9a8bd6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,7 +126,7 @@
         <global_solr_version>6.6.2</global_solr_version>
         <global_mockito_version>1.10.19</global_mockito_version>
         <global_powermock_version>1.7.0</global_powermock_version>
-        <global_shade_version>3.1.1</global_shade_version>
+        <global_shade_version>3.2.0</global_shade_version>
         <global_jackson_version>2.7.4</global_jackson_version>
         <global_errorprone_core_version>2.0.14</global_errorprone_core_version>
         <global_jar_version>3.0.2</global_jar_version>
@@ -137,8 +137,9 @@
         <global_reflections_version>0.9.10</global_reflections_version>
         <global_checkstyle_version>8.0</global_checkstyle_version>
         <global_log4j_core_version>2.1</global_log4j_core_version>
-        <global_simple_syslog_version>0.0.8</global_simple_syslog_version>
+        <global_simple_syslog_version>0.0.9</global_simple_syslog_version>
         <global_spark_version>2.3.1</global_spark_version>
+        <global_httpclient_version>4.3.2</global_httpclient_version>
     </properties>
 
     <profiles>

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/site/develop/bylaws.md
----------------------------------------------------------------------
diff --git a/site/develop/bylaws.md b/site/develop/bylaws.md
deleted file mode 100644
index a8fc5fb..0000000
--- a/site/develop/bylaws.md
+++ /dev/null
@@ -1,281 +0,0 @@
----
-layout: page
-title: Apache Metron Bylaws
----
-
-## Introduction
-
-This document defines the bylaws under which the Apache Metron project
-operates. It defines the roles and responsibilities of the project,
-who may vote, how voting works, how conflicts are resolved, etc.
-
-Metron is a project of the Apache Software Foundation (ASF) and the foundation
-holds the trademark on the name "Metron" and copyright on the combined
-code base. The [Apache Foundation
-FAQ](http://www.apache.org/foundation/faq.html) and
-[How-It-Works](http://www.apache.org/foundation/how-it-works.html)
-explain the operation and background of the foundation.
-
-Apache has a [code of
-conduct](http://www.apache.org/foundation/policies/conduct.html) that
-it expects its members to follow. In particular:
-
-* Be **open and welcoming**. It is important that we grow and
-  encourage the community of users and developers for our project.
-
-* Be **collaborative**. Working together on the open mailing lists and
-  bug database to make decisions helps the project grow.
-
-* Be **respectful** of others. Everyone is volunteering their time and
-  efforts to work on this project. Please be respectful of everyone
-  and their views.
-
-Metron is typical of Apache projects in that it operates under a set of
-principles, known collectively as the "Apache Way". If you are new to
-Apache development, please refer to
-[this](http://www.apache.org/foundation/how-it-works.html) for more
-information on how Apache projects operate.
-
-## Roles and Responsibilities
-
-Apache projects define a set of roles with associated rights and
-responsibilities. These roles govern what tasks an individual may
-perform within the project. The roles are defined in the following
-sections.
-
-### Users
-
-The most important participants in the project are people who use our
-software. The majority of our developers start out as users and guide
-their development efforts from the user's perspective.  Users
-contribute to the Apache projects by providing feedback to developers
-in the form of bug reports and feature suggestions. As well, users
-participate in the Apache community by helping other users on mailing
-lists and user support forums.
-
-### Contributors
-
-Contributors include all of the volunteers who donate time, code,
-documentation,
-or resources to the Metron Project. A contributor that makes sustained,
-welcome contributions to the project may be invited to become a
-committer, though the exact timing of such invitations depends on many
-factors.
-
-### Committers
-
-The project's committers are responsible for the project's technical
-management. Committers have the right to commit to the project's git
-repository. Committers may cast binding votes on any technical
-discussion.
-
-Committer access is by invitation only and must be approved by
-consensus approval of the active Project Management Committee (PMC)
-members.
-
-If a committer wishes to leave the project or does not contribute to
-the project in any form for six months, the PMC may make them emeritus.
-Emeritus committers lose their ability to commit code or cast binding
-votes. An emeritus committer may
-request reinstatement of commit access from the PMC. Such
-reinstatement is subject to consensus approval of active PMC members.
-
-All Apache committers are required to have a signed [Individual
-Contributor License
-Agreement](https://www.apache.org/licenses/icla.txt) (ICLA) on file
-with the Apache Software Foundation. There is a [Committer
-FAQ](http://www.apache.org/dev/committers.html) which provides more
-details on the requirements for Committers.
-
-A committer who makes a
-sustained contribution to the project may be invited to become a
-member of the PMC. The form of contribution
-is not limited to code. It can also include code review, helping out
-users on the mailing lists, documentation, testing, etc.
-
-### Release Manager
-
-A Release Manager (RM) is a committer who volunteers to produce a
-Release Candidate. The RM shall publish a Release Plan on the
-dev mailing list stating the branch from which they intend to
-make a Release Candidate.
-
-### Project Management Committee
-
-The Project Management Committee (PMC) for Apache Metron was created by
-the Apache Board in December 2015 when Metron moved out of Cisco's OpenSOC  
-project and became an incubated project at Apache.
-The PMC is responsible to the board and
-the ASF for the management and oversight of the Apache Metron
-codebase. The responsibilities of the PMC include
-
-  * Deciding what is distributed as products of the Apache Metron
-    project. In particular all releases must be approved by the PMC.
-
-  * Maintaining the project's shared resources, including the codebase
-    repository, mailing lists, and websites.
-
-  * Speaking on behalf of the project.
-
-  * Resolving license disputes regarding products of the project
-
-  * Nominating new PMC members and committers
-
-  * Maintaining these bylaws and other guidelines of the project
-
-Membership of the PMC is by invitation only and must be approved by a
-consensus approval of active PMC members.
-
-A PMC member is considered
-emeritus by their own declaration or by not contributing in any form
-to the project for over six months. An emeritus member may request
-reinstatement to the PMC. Such reinstatement is subject to consensus
-approval of the active PMC members.
-
-The chair of the PMC is appointed by the ASF board. The chair is an
-office holder of the Apache Software Foundation (Vice President,
-Apache Metron) and has primary responsibility to the board for the
-management of the project within the scope of the Metron PMC. The
-chair reports to the board quarterly on developments within the Metron
-project.
-
-When the project desires a new PMC chair, the PMC votes to recommend a
-new chair using [Single Transferable
-Vote](http://wiki.apache.org/general/BoardVoting) voting. The decision
-must be ratified by the Apache board.
-
-## Decision Making
-
-Within the Metron project, different types of decisions require
-different forms of approval. For example, the previous section
-describes several decisions which require "consensus approval."
-This section defines how voting is performed, the types of
-approvals, and which types of decision require which type of approval.
-
-### Voting
-
-Decisions regarding the project are made by votes on the primary
-project development mailing list (d...@metron.apache.org). Where
-necessary, PMC voting may take place on the private Metron PMC mailing
-list. Votes are clearly indicated by subject line starting with
-[VOTE]. Votes may contain multiple items for approval and these should
-be clearly separated. Voting is carried out by replying to the vote
-mail. Voting may take five flavors:
-
-* **+1** -- "Yes," "Agree," or "the action should be performed." In general,
-  this vote also indicates a willingness on the behalf of the voter in
-  "making it happen."
-
-* **+0** -- This vote indicates a willingness for the action under
-  consideration to go ahead. The voter, however, will not be able to
-  help.
-
-* **0** -- The voter is neutral on the topic under discussion.
-
-* **-0** -- This vote indicates that the voter does not, in general, agree
-   with the proposed action but is not concerned enough to prevent the
-   action going ahead.
-
-* **-1** -- This is a negative vote. On issues where consensus is required,
-   this vote counts as a veto. All vetoes must contain an explanation
-   of why the veto is appropriate. Vetoes with no explanation are
-   void. It may also be appropriate for a -1 vote to include an
-   alternative course of action.
-
-All participants in the Metron project are encouraged to show their
-agreement for or against a particular action by voting, regardless of
-whether their vote is binding. Nonbinding votes are useful for
-encouraging discussion and understanding the scope of opinions within
-the project.
-
-### Approvals
-
-These are the types of approvals that can be sought. Different actions
-require different types of approvals.
-
-* **Consensus Approval** -- Consensus approval requires 3 binding +1
-  votes and no binding vetoes.
-
-* **Lazy Consensus** -- Lazy consensus requires at least one +1 vote and
-  no -1 votes ('silence gives assent').
-
-* **Lazy Majority** -- A lazy majority vote requires 3 binding +1 votes
-   and more binding +1 votes than -1 votes.
-
-* **Lazy 2/3 Majority** -- Lazy 2/3 majority votes requires at least 3
-  votes and twice as many +1 votes as -1 votes.
-
-### Vetoes
-
-A valid, binding veto cannot be overruled. If a veto is cast, it must
-be accompanied by a valid reason explaining the reasons for the
-veto. The validity of a veto, if challenged, can be confirmed by
-anyone who has a binding vote. This does not necessarily signify
-agreement with the veto - merely that the veto is valid.  If you
-disagree with a valid veto, you must lobby the person casting the veto
-to withdraw their veto. If a veto is not withdrawn, any action that
-has already been taken  must be reversed in a timely manner.
-
-### Actions
-
-This section describes the various actions which are undertaken within
-the project, the corresponding approval required for that action and
-those who have binding votes over the action.
-
-#### Code Change
-
-A change made to a codebase of the project requires *lazy consensus*
-of active committers other than the author of the patch. The code can
-be committed after the first +1.
-
-#### Product Release
-
-To make a release, the release manager creates a release candidate and
-a vote requiring a *lazy majority* of the active PMC members is
-required. Once the vote passes, the release candidate becomes an
-official release.
-
-#### Adoption of New Codebase
-
-When the codebase for an existing, released product is to be replaced
-with an alternative codebase, it requires a *lazy 2/3 majority* of PMC
-members. This also covers the creation of new sub-projects and
-submodules within the project.
-
-#### New Committer
-
-When a new committer is proposed for the project, *consensus approval*
-of the active PMC members is required.
-
-#### New PMC Member
-
-To promote a committer to a PMC member requires *consensus approval*
-of active PMC members.
-
-If the vote passes, the Apache Board must be notified to make the change
-official.
-
-#### Committer Removal
-
-Removal of commit privileges requires a *lazy 2/3 majority* of active
-PMC members.
-
-#### PMC Member Removal
-
-Removing a PMC member requires a *lazy 2/3 majority* of active PMC
-members, excluding the member in question.
-
-If the vote passes, the Apache Board must be notified to make the change
-official.
-
-#### Modifying Bylaws
-
-Modifying this document requires a *lazy majority* of active PMC members.
-
-### Voting Timeframes
-
-Votes are open for a minimum period of 72 hours to allow all active
-voters time to consider the vote. For holiday weekends or conferences,
-consider using a longer vote window. Votes relating to code changes are
-not subject to a strict timetable but should be made as timely as
-possible.

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/site/develop/coding.md
----------------------------------------------------------------------
diff --git a/site/develop/coding.md b/site/develop/coding.md
deleted file mode 100644
index 78c7e27..0000000
--- a/site/develop/coding.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-layout: page
-title: Coding Guidelines
----
-
-## General rules
-
-* All files must have an Apache copyright header at the top of the file.
-* Code should be removed rather than commented out.
-* All public functions should have javadoc comments.
-* Always use braces to surround branches.
-* try-finally should be avoided.
-
-## Formatting
-
-* All files must have an 80 character maximum line length.
-* Indentation should be 2 spaces.
-* Files should use spaces instead of tabs.
-* Wrapping lines
-  * Break after a comma.
-  * Break before an operator.
-  * Prefer higher-level breaks to lower-level breaks.
-  * Align the new line with beginning of the expression at the same level
-    on the previous line.
-  * If the above rules lead to confusing code, just indent 8 spaces.
-* One variable declaration per a line.
-
-## Naming
-
-* Packages should be all lowercase.
-  * Java code should be in `org.apache.,metron`, except for compatibility 
classes 
-* Classes should be in mixed case.
-* Variables should be in camel case.
-* Constants should be in upper case.

http://git-wip-us.apache.org/repos/asf/metron/blob/8bf3b6ec/site/develop/index.md
----------------------------------------------------------------------
diff --git a/site/develop/index.md b/site/develop/index.md
deleted file mode 100644
index 7db04fa..0000000
--- a/site/develop/index.md
+++ /dev/null
@@ -1,61 +0,0 @@
----
-layout: page
-title: Developing
----
-
-Information about the Metron project that is most important for
-developers working on the project. The project has created
-[bylaws](bylaws.html) for itself.
-
-## Project Members
-
-Name                    | Apache Id      | Role
-:---------------------- | :------------- | :---
-Owen O'Malley           | omalley        | PMC
-Jim Baker               | jimbaker       | PMC
-Mark Bittmann           | mbittmann      | PMC
-Sheetal Dolas           | sheetal_dolas  | PMC
-P. Taylor Goetz         | ptgoetz        | PMC
-Brad Kolarov            | billie         | PMC
-Dave Hirko              | dbhirko        | PMC
-Larry McCay             | lmccay         | PMC
-Charles Porter          | cporter        | PMC
-James Sirota            | jsirota        | PMC
-Casey Stella            | cestella       | PMC
-Vinod Kumar Vavilapalli | vinodkv        | PMC
-George Vetticaden       | gvetticaden    | PMC
-
-## Mailing Lists
-
-There are several development mailing lists for Metron
-
-* [d...@metron.apache.org](mailto:d...@metron.apache.org) - Development 
discussions
-  with archive [here](https://mail-archives.apache.org/mod_mbox/metron-dev/)
-* [iss...@metron.apache.org](mailto:iss...@metron.apache.org) - Bug tracking
-  with archive [here](https://mail-archives.apache.org/mod_mbox/metron-issues/)
-* [commits@metron.apache.org](mailto:commits@metron.apache.org) - Git tracking
-  with archive 
[here](https://mail-archives.apache.org/mod_mbox/metron-commits/)
-
-You can subscribe to the lists by sending email to
-*list*-subscr...@metron.apache.org and unsubscribe by sending email to
-*list*-unsubscr...@metron.apache.org.
-
-## Source code
-
-Metron uses git for version control. Get the source code:
-
-`% git clone https://git-wip-us.apache.org/repos/asf/metron.git`
-
-The important branches are:
-
-* [master](https://github.com/apache/metron/tree/master) -
-  The trunk for all developement
-* [asf-site](https://github.com/apache/metron/tree/asf-site) -
-  The pages that are deployed to https://metron.apache.org/
-
-Please check our [coding guidelines](/develop/coding.html).
-
-## Reviews
-
-All code must be +1'ed by a committer other than the author prior to its
-commit.

Reply via email to