You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fluo.apache.org by kt...@apache.org on 2018/04/04 14:30:44 UTC

[fluo] 01/03: Scan command now can produce results as csv and json. #984 #1018

This is an automated email from the ASF dual-hosted git repository.

kturner pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/fluo.git

commit 0ec21c92153b916f92ca44b38dca0700ac09d841
Author: Alan Camillo <al...@blueshift.com.br>
AuthorDate: Wed Apr 4 10:23:02 2018 -0400

    Scan command now can produce results as csv and json. #984 #1018
---
 .../org/apache/fluo/cluster/runner/AppRunner.java  |  25 ++-
 modules/command/pom.xml                            |   4 +
 .../java/org/apache/fluo/command/FluoScan.java     |  89 +++++++-
 modules/core/pom.xml                               |   8 +
 .../java/org/apache/fluo/core/util/ScanUtil.java   | 244 ++++++++++++++++-----
 modules/distribution/src/main/lib/fetch.sh         |   9 +-
 pom.xml                                            |  14 +-
 7 files changed, 326 insertions(+), 67 deletions(-)

diff --git a/modules/cluster/src/main/java/org/apache/fluo/cluster/runner/AppRunner.java b/modules/cluster/src/main/java/org/apache/fluo/cluster/runner/AppRunner.java
index 9de17f4..5be9ef1 100644
--- a/modules/cluster/src/main/java/org/apache/fluo/cluster/runner/AppRunner.java
+++ b/modules/cluster/src/main/java/org/apache/fluo/cluster/runner/AppRunner.java
@@ -15,6 +15,7 @@
 
 package org.apache.fluo.cluster.runner;
 
+import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Collections;
@@ -72,10 +73,15 @@ public abstract class AppRunner {
       System.exit(0);
     }
 
-    if (options.scanAccumuloTable) {
-      ScanUtil.scanAccumulo(options.getScanOpts(), config);
-    } else {
-      ScanUtil.scanFluo(options.getScanOpts(), config);
+    try {
+      if (options.scanAccumuloTable) {
+        ScanUtil.scanAccumulo(options.getScanOpts(), config, System.out);
+      } else {
+        ScanUtil.scanFluo(options.getScanOpts(), config, System.out);
+      }
+    } catch (IOException e) {
+      System.err.println(e.getMessage());
+      System.exit(-1);
     }
   }
 
@@ -203,6 +209,14 @@ public abstract class AppRunner {
             + "internal schema, making it easier to comprehend.")
     public boolean scanAccumuloTable = false;
 
+    public boolean exportAsCsv = false;
+    public String csvHeader;
+    public String csvDelimiter;
+    public String csvEscape;
+    public String csvQuote;
+    public String csvQuoteMode;
+    public boolean exportAsJson = false;
+
     public String getStartRow() {
       return startRow;
     }
@@ -228,7 +242,8 @@ public abstract class AppRunner {
 
     public ScanUtil.ScanOpts getScanOpts() {
       return new ScanUtil.ScanOpts(startRow, endRow, columns, exactRow, rowPrefix, help,
-          hexEncNonAscii, scanAccumuloTable);
+          hexEncNonAscii, scanAccumuloTable, exportAsCsv, csvDelimiter, csvEscape, csvHeader,
+          csvQuote, csvQuoteMode, exportAsJson);
     }
   }
 }
diff --git a/modules/command/pom.xml b/modules/command/pom.xml
index 8c8bf46..f9949a2 100644
--- a/modules/command/pom.xml
+++ b/modules/command/pom.xml
@@ -47,6 +47,10 @@
       <artifactId>accumulo-core</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.curator</groupId>
       <artifactId>curator-framework</artifactId>
     </dependency>
diff --git a/modules/command/src/main/java/org/apache/fluo/command/FluoScan.java b/modules/command/src/main/java/org/apache/fluo/command/FluoScan.java
index 4d56194..9f4455a 100644
--- a/modules/command/src/main/java/org/apache/fluo/command/FluoScan.java
+++ b/modules/command/src/main/java/org/apache/fluo/command/FluoScan.java
@@ -15,10 +15,12 @@
 
 package org.apache.fluo.command;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 
 import com.beust.jcommander.Parameter;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.fluo.api.config.FluoConfiguration;
 import org.apache.fluo.core.client.FluoAdminImpl;
 import org.apache.fluo.core.util.ScanUtil;
@@ -54,6 +56,35 @@ public class FluoScan {
             + "internal schema, making it easier to comprehend.")
     public boolean scanAccumuloTable = false;
 
+    @Parameter(names = "--csv", help = true,
+        description = "Export key/values stored in Accumulo as CSV file. Uses Fluo application "
+            + "properties to configure the CSV format.")
+    public boolean exportAsCsv = false;
+
+    @Parameter(names = "--csv-header", help = true, description = "Set header for \"true\".")
+    public String csvHeader;
+
+    @Parameter(names = "--csv-delimiter", help = true,
+        description = "Configure delimiter to a designeted character.")
+    public String csvDelimiter;
+
+    @Parameter(names = "--csv-escape", help = true,
+        description = "Configure escape to a designeted character.")
+    public String csvEscape;
+
+    @Parameter(names = "--csv-quote", help = true,
+        description = "Configure quote to a designeted character.")
+    public String csvQuote;
+
+    @Parameter(names = "--csv-quote-mode", help = true,
+        description = "Configure quote mode to a designeted mode. The possible "
+                + "modes are: ALL, ALL_NON_NULL, MINIMAL, NONE and NON_NUMERIC")
+    public String csvQuoteMode;
+
+    @Parameter(names = "--json", help = true,
+        description = "Export key/values stored in Accumulo as JSON file.")
+    public boolean exportAsJson = false;
+
     public String getStartRow() {
       return startRow;
     }
@@ -70,6 +101,26 @@ public class FluoScan {
       return rowPrefix;
     }
 
+    public String getCsvHeader() {
+      return csvHeader;
+    }
+
+    public String getCsvDelimiter() {
+      return csvDelimiter;
+    }
+
+    public String getCsvEscape() {
+      return csvEscape;
+    }
+
+    public String getCsvQuote() {
+      return csvQuote;
+    }
+
+    public String getCsvQuoteMode() {
+      return csvQuoteMode;
+    }
+
     public List<String> getColumns() {
       if (columns == null) {
         return Collections.emptyList();
@@ -77,9 +128,27 @@ public class FluoScan {
       return columns;
     }
 
+    /**
+     * Check if the parameters informed can be used together.
+     * @since 1.2
+     */
+    private void checkScanOptions() {
+      if (this.exportAsCsv && this.exportAsJson) {
+        throw new IllegalArgumentException(
+            "Both \"--csv\" and \"--json\" can not be set together.");
+      }
+
+      if (!this.exportAsCsv && (StringUtils.isNotEmpty(this.csvDelimiter)
+          | StringUtils.isNotEmpty(this.csvEscape) | StringUtils.isNotEmpty(this.csvHeader)
+          | StringUtils.isNotEmpty(this.csvQuote) | StringUtils.isNotEmpty(this.csvQuoteMode))) {
+        throw new IllegalArgumentException("No \"--csv\" detected");
+      }
+    }
+
     public ScanUtil.ScanOpts getScanOpts() {
       return new ScanUtil.ScanOpts(startRow, endRow, columns, exactRow, rowPrefix, help,
-          hexEncNonAscii, scanAccumuloTable);
+          hexEncNonAscii, scanAccumuloTable, exportAsCsv, csvDelimiter, csvEscape, csvHeader,
+          csvQuote, csvQuoteMode, exportAsJson);
     }
 
     public static ScanOptions parse(String[] args) {
@@ -95,16 +164,24 @@ public class FluoScan {
     Logger.getLogger("org.apache.fluo").setLevel(Level.ERROR);
 
     ScanOptions options = ScanOptions.parse(args);
+    options.checkScanOptions();
     FluoConfiguration config = CommandUtil.resolveFluoConfig();
     config.setApplicationName(options.getApplicationName());
     options.overrideFluoConfig(config);
     CommandUtil.verifyAppRunning(config);
 
-    if (options.scanAccumuloTable) {
-      config = FluoAdminImpl.mergeZookeeperConfig(config);
-      ScanUtil.scanAccumulo(options.getScanOpts(), config);
-    } else {
-      ScanUtil.scanFluo(options.getScanOpts(), config);
+    try {
+      options.overrideFluoConfig(config);
+      if (options.scanAccumuloTable) {
+        config = FluoAdminImpl.mergeZookeeperConfig(config);
+        ScanUtil.scanAccumulo(options.getScanOpts(), config, System.out);
+      } else {
+        ScanUtil.scanFluo(options.getScanOpts(), config, System.out);
+      }
+    } catch (RuntimeException | IOException e) {
+      System.err.println("Scan failed - " + e.getMessage());
+      System.exit(-1);
     }
   }
+
 }
diff --git a/modules/core/pom.xml b/modules/core/pom.xml
index fdd1f69..0cfc6b3 100644
--- a/modules/core/pom.xml
+++ b/modules/core/pom.xml
@@ -50,6 +50,14 @@
       <artifactId>accumulo-core</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-csv</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.curator</groupId>
       <artifactId>curator-client</artifactId>
     </dependency>
diff --git a/modules/core/src/main/java/org/apache/fluo/core/util/ScanUtil.java b/modules/core/src/main/java/org/apache/fluo/core/util/ScanUtil.java
index 46aacca..78c3efb 100644
--- a/modules/core/src/main/java/org/apache/fluo/core/util/ScanUtil.java
+++ b/modules/core/src/main/java/org/apache/fluo/core/util/ScanUtil.java
@@ -15,15 +15,31 @@
 
 package org.apache.fluo.core.util;
 
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.DateFormat;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import com.google.common.collect.Iterables;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonIOException;
 import org.apache.accumulo.core.client.Connector;
 import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.security.Authorizations;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+import org.apache.commons.csv.QuoteMode;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.fluo.accumulo.format.FluoFormatter;
 import org.apache.fluo.api.client.FluoClient;
 import org.apache.fluo.api.client.FluoFactory;
@@ -37,6 +53,16 @@ import org.apache.fluo.api.data.Span;
 import org.apache.fluo.api.exceptions.FluoException;
 
 public class ScanUtil {
+  public static final String CSV_HEADER = "csv.header";
+  public static final String CSV_QUOTE_MODE = "csv.quoteMode";
+  public static final String CSV_QUOTE = "csv.quote";
+  public static final String CSV_ESCAPE = "csv.escape";
+  public static final String CSV_DELIMITER = "csv.delimiter";
+  public static final String FLUO_VALUE = "value";
+  public static final String FLUO_COLUMN_VISIBILITY = "visibility";
+  public static final String FLUO_COLUMN_QUALIFIER = "qualifier";
+  public static final String FLUO_COLUMN_FAMILY = "family";
+  public static final String FLUO_ROW = "row";
 
   public static Span getSpan(ScanOpts options) {
     Span span = new Span();
@@ -89,68 +115,170 @@ public class ScanUtil {
     return columns;
   }
 
-  public static void scanFluo(ScanOpts options, FluoConfiguration sConfig) {
+  public static void scanFluo(ScanOpts options, FluoConfiguration sConfig, PrintStream out)
+      throws IOException {
 
     try (FluoClient client = FluoFactory.newClient(sConfig)) {
       try (Snapshot s = client.newSnapshot()) {
 
-        Span span = null;
-        Collection<Column> columns = null;
-        try {
-          span = getSpan(options);
-          columns = getColumns(options);
-        } catch (IllegalArgumentException e) {
-          System.err.println(e.getMessage());
-          System.exit(-1);
+        Span span = getSpan(options);
+        Collection<Column> columns = getColumns(options);
+
+        if (options.exportAsJson) {
+          generateJson(options, span, columns, s, out);
+        } else { // TSV or CSV format
+          generateTsvCsv(options, span, columns, s, out);
         }
 
-        CellScanner cellScanner = s.scanner().over(span).fetch(columns).build();
-
-        StringBuilder sb = new StringBuilder();
-        for (RowColumnValue rcv : cellScanner) {
-          if (options.hexEncNonAscii) {
-            sb.setLength(0);
-            Hex.encNonAscii(sb, rcv.getRow());
-            sb.append(" ");
-            Hex.encNonAscii(sb, rcv.getColumn(), " ");
-            sb.append("\t");
-            Hex.encNonAscii(sb, rcv.getValue());
-            System.out.println(sb.toString());
-          } else {
-            sb.setLength(0);
-            sb.append(rcv.getsRow());
-            sb.append(" ");
-            sb.append(rcv.getColumn());
-            sb.append("\t");
-            sb.append(rcv.getsValue());
-            System.out.println(sb.toString());
-          }
+      } catch (FluoException e) {
+        throw e;
+      }
+    }
+  }
 
-          if (System.out.checkError()) {
-            break;
-          }
+  /**
+   * Generate TSV or CSV format as result of the scan.
+   * 
+   * @since 1.2
+   */
+  private static void generateTsvCsv(ScanOpts options, Span span, Collection<Column> columns,
+      final Snapshot snapshot, PrintStream out) throws IOException {
+    // CSV Formater
+    CSVFormat csvFormat = CSVFormat.DEFAULT;
+    csvFormat = csvFormat.withQuoteMode(QuoteMode.ALL);
+    csvFormat = csvFormat.withRecordSeparator("\n");
+
+    // when "--csv" parameter is passed the "fluo.scan.csv" is analised
+    if (options.exportAsCsv) {
+      if (StringUtils.isNotEmpty(options.csvDelimiter)) {
+        if (options.csvDelimiter.length() > 1) {
+          throw new IllegalArgumentException(
+              "Invalid character for the \"--csv-delimiter\" parameter.");
+        }
+        csvFormat = csvFormat.withDelimiter(options.csvDelimiter.charAt(0));
+      }
+
+      if (StringUtils.isNotEmpty(options.csvEscape)) {
+        if (options.csvEscape.length() > 1) {
+          throw new IllegalArgumentException(
+              "Invalid character for the \"--csv-escape\" parameter.");
         }
+        csvFormat = csvFormat.withEscape(options.csvEscape.charAt(0));
+      }
 
-      } catch (FluoException e) {
-        System.err.println("Scan failed - " + e.getMessage());
-        System.exit(-1);
+      if (StringUtils.isNotEmpty(options.csvQuote)) {
+        if (options.csvQuote.length() > 1) {
+          throw new IllegalArgumentException(
+              "Invalid character for the \"--csv-quote\" parameter.");
+        }
+        csvFormat = csvFormat.withQuote(options.csvQuote.charAt(0));
+      }
+
+      // It can throw "java.lang.IllegalArgumentException" if the value not exists
+      // in "org.apache.commons.csv.QuoteMode"
+      if (StringUtils.isNotEmpty(options.csvQuoteMode)) {
+        csvFormat = csvFormat.withQuoteMode(QuoteMode.valueOf(options.csvQuoteMode));
+      }
+
+      if (BooleanUtils.toBooleanObject(
+          ObjectUtils.defaultIfNull(options.csvHeader, Boolean.FALSE.toString()))) {
+        csvFormat = csvFormat.withHeader(FLUO_ROW, FLUO_COLUMN_FAMILY, FLUO_COLUMN_QUALIFIER,
+            FLUO_COLUMN_VISIBILITY, FLUO_VALUE);
+      }
+    } else {
+      // Default TAB separator and NO quotes if possible.
+      csvFormat = csvFormat.withDelimiter(CSVFormat.TDF.getDelimiter());
+      csvFormat = csvFormat.withQuoteMode(QuoteMode.MINIMAL);
+    }
+
+    try (CSVPrinter printer = new CSVPrinter(out, csvFormat)) {
+      CellScanner cellScanner = snapshot.scanner().over(span).fetch(columns).build();
+
+      List<Object> record = new LinkedList<>();
+      StringBuilder sb = new StringBuilder();
+      int lines2check = 0;
+      for (RowColumnValue rcv : cellScanner) {
+        record.clear();
+        if (options.hexEncNonAscii) {
+          sb.setLength(0);
+          Hex.encNonAscii(sb, rcv.getRow());
+          record.add(sb.toString());
+          sb.setLength(0);
+          Hex.encNonAscii(sb, rcv.getColumn().getFamily());
+          record.add(sb.toString());
+          sb.setLength(0);
+          Hex.encNonAscii(sb, rcv.getColumn().getQualifier());
+          record.add(sb.toString());
+          sb.setLength(0);
+          Hex.encNonAscii(sb, rcv.getColumn().getVisibility());
+          record.add(sb.toString());
+          sb.setLength(0);
+          Hex.encNonAscii(sb, rcv.getValue());
+          record.add(sb.toString());
+        } else {
+          record.add(rcv.getsRow());
+          record.add(rcv.getColumn().getFamily());
+          record.add(rcv.getColumn().getQualifier());
+          record.add(rcv.getColumn().getVisibility());
+          record.add(rcv.getsValue());
+        }
+
+        printer.printRecord(record);
+        lines2check++;
+        if (lines2check == 100) {
+          lines2check = 0;
+          if (out.checkError()) {
+            throw new IOException("Fail to write data to stream.");
+          }
+        }
       }
     }
+    out.flush();
   }
 
-  public static void scanAccumulo(ScanOpts options, FluoConfiguration sConfig) {
+  /**
+   * Generate JSON format as result of the scan.
+   * 
+   * @since 1.2
+   */
+  private static void generateJson(ScanOpts options, Span span, Collection<Column> columns,
+      final Snapshot snapshot, PrintStream out) throws JsonIOException {
+    Gson gson = new GsonBuilder().serializeNulls().setDateFormat(DateFormat.LONG)
+        .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setVersion(1.0)
+        .create();
 
-    Connector conn = AccumuloUtil.getConnector(sConfig);
+    CellScanner cellScanner = snapshot.scanner().over(span).fetch(columns).build();
 
-    Span span = null;
-    Collection<Column> columns = null;
-    try {
-      span = getSpan(options);
-      columns = getColumns(options);
-    } catch (IllegalArgumentException e) {
-      System.err.println(e.getMessage());
-      System.exit(-1);
+    StringBuilder sb = new StringBuilder();
+    Map<String, String> json = new LinkedHashMap<>();
+    for (RowColumnValue rcv : cellScanner) {
+      sb.setLength(0);
+      Hex.encNonAscii(sb, rcv.getRow());
+      json.put(FLUO_ROW, sb.toString());
+      sb.setLength(0);
+      Hex.encNonAscii(sb, rcv.getColumn().getFamily());
+      json.put(FLUO_COLUMN_FAMILY, sb.toString());
+      Hex.encNonAscii(sb, rcv.getColumn().getQualifier());
+      json.put(FLUO_COLUMN_QUALIFIER, sb.toString());
+      sb.setLength(0);
+      Hex.encNonAscii(sb, rcv.getColumn().getVisibility());
+      json.put(FLUO_COLUMN_VISIBILITY, sb.toString());
+      sb.setLength(0);
+      Hex.encNonAscii(sb, rcv.getValue());
+      json.put(FLUO_VALUE, sb.toString());
+
+      gson.toJson(json, out);
+      out.append("\n");
     }
+    out.flush();
+  }
+
+  public static void scanAccumulo(ScanOpts options, FluoConfiguration sConfig, PrintStream out) {
+
+    Connector conn = AccumuloUtil.getConnector(sConfig);
+
+    Span span = getSpan(options);
+    Collection<Column> columns = getColumns(options);
 
     try {
       Scanner scanner = conn.createScanner(sConfig.getAccumuloTable(), Authorizations.EMPTY);
@@ -165,11 +293,11 @@ public class ScanUtil {
       }
 
       for (String entry : Iterables.transform(scanner, FluoFormatter::toString)) {
-        System.out.println(entry);
+        out.println(entry);
       }
+      out.flush();
     } catch (Exception e) {
-      System.err.println("Scan failed - " + e.getMessage());
-      System.exit(-1);
+      throw new RuntimeException(e);
     }
   }
 
@@ -183,9 +311,18 @@ public class ScanUtil {
     public boolean help;
     public boolean hexEncNonAscii = true;
     public boolean scanAccumuloTable = false;
+    public boolean exportAsCsv = false;
+    public boolean exportAsJson = false;
+    public final String csvDelimiter;
+    public final String csvEscape;
+    public final String csvHeader;
+    public final String csvQuote;
+    public final String csvQuoteMode;
 
     public ScanOpts(String startRow, String endRow, List<String> columns, String exactRow,
-        String rowPrefix, boolean help, boolean hexEncNonAscii, boolean scanAccumuloTable) {
+        String rowPrefix, boolean help, boolean hexEncNonAscii, boolean scanAccumuloTable,
+        boolean exportAsCsv, String csvDelimiter, String csvEscape, String csvHeader,
+        String csvQuote, String csvQuoteMode, boolean exportAsJson) {
       this.startRow = startRow;
       this.endRow = endRow;
       this.columns = columns;
@@ -194,6 +331,13 @@ public class ScanUtil {
       this.help = help;
       this.hexEncNonAscii = hexEncNonAscii;
       this.scanAccumuloTable = scanAccumuloTable;
+      this.exportAsCsv = exportAsCsv;
+      this.csvDelimiter = csvDelimiter;
+      this.csvEscape = csvEscape;
+      this.csvHeader = csvHeader;
+      this.csvQuote = csvQuote;
+      this.csvQuoteMode = csvQuoteMode;
+      this.exportAsJson = exportAsJson;
     }
 
     public String getStartRow() {
diff --git a/modules/distribution/src/main/lib/fetch.sh b/modules/distribution/src/main/lib/fetch.sh
index d945d83..51a54f4 100755
--- a/modules/distribution/src/main/lib/fetch.sh
+++ b/modules/distribution/src/main/lib/fetch.sh
@@ -17,7 +17,7 @@ lib_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
 maven_prefix=https://repo1.maven.org/maven2
 
 function download {
-  IFS=':' read -ra DEP <<< "$1" 
+  IFS=':' read -ra DEP <<< "$1"
   dir=$lib_dir/
   if [ -n "$2" ]; then
     dir=$lib_dir/$2
@@ -35,7 +35,7 @@ function download {
 
   if [ -f $dir/$fn ]; then
     echo "SUCCESS: Dependency exists - $dir/$fn"
-  else 
+  else
     wget -q $download_url -P $dir
     if [ $? == 0 ]; then
       echo "SUCCESS: Dependency downloaded from $download_url"
@@ -56,12 +56,14 @@ extra)
   echo "Fetching extra Fluo dependencies"
   download aopalliance:aopalliance:jar:1.0
   download com.beust:jcommander:jar:1.32
-  download com.google.code.gson:gson:jar:2.2.4
+  download com.google.code.gson:gson:jar:2.8.0
   download com.google.guava:guava:jar:13.0.1
   download com.google.inject:guice:jar:4.0
   download commons-collections:commons-collections:jar:3.2.1
   download commons-configuration:commons-configuration:jar:1.10
   download commons-io:commons-io:jar:2.4
+  download org.apache.commons:commons-csv:jar:1.5
+  download org.apache.commons:commons-lang3:jar:3.7
   download io.dropwizard.metrics:metrics-core:jar:3.1.1
   download io.dropwizard.metrics:metrics-graphite:jar:3.1.1
   download javax.inject:javax.inject:jar:1
@@ -117,4 +119,3 @@ extra)
   echo -e "However, you can override them using the command below:\n"
   echo "./fetch.sh ahz -Daccumulo.version=1.7.2 -Dhadoop.version=2.7.2 -Dzookeeper.version=3.4.8"
 esac
-
diff --git a/pom.xml b/pom.xml
index ccd069e..7307daa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,9 +5,9 @@
   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
@@ -140,6 +140,16 @@
         <version>${accumulo.version}</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-csv</artifactId>
+        <version>1.5</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-lang3</artifactId>
+        <version>3.7</version>
+      </dependency>
+      <dependency>
         <groupId>org.apache.curator</groupId>
         <artifactId>curator-client</artifactId>
         <version>${curator.version}</version>

-- 
To stop receiving notification emails like this one, please contact
kturner@apache.org.