You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by pv...@apache.org on 2018/02/28 16:25:03 UTC

[09/20] nifi git commit: NIFI-4839 - Modified how the process group box is calculated - Adding command to get the id of a registry client by name - Refactoring how results are written to support option of simple or json output - Added pg-set-var command

NIFI-4839
- Modified how the process group box is calculated
- Adding command to get the id of a registry client by name
- Refactoring how results are written to support option of simple or json output
- Added pg-set-var command
- Added pg-list command
- Added getDescription to every command and prints when asking for help on a command
- Adding verbose out to help command to print description for every command


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/c1c80800
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/c1c80800
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/c1c80800

Branch: refs/heads/master
Commit: c1c808002c32008f31ec80664046c1857782e5a9
Parents: e3cc7be
Author: Bryan Bende <bb...@apache.org>
Authored: Wed Feb 7 09:42:20 2018 -0500
Committer: Pierre Villard <pi...@gmail.com>
Committed: Wed Feb 28 17:23:10 2018 +0100

----------------------------------------------------------------------
 nifi-toolkit/nifi-toolkit-assembly/LICENSE      |  37 ++++
 nifi-toolkit/nifi-toolkit-cli/README.md         |  36 +++-
 nifi-toolkit/nifi-toolkit-cli/pom.xml           |   4 -
 .../org/apache/nifi/toolkit/cli/CLIMain.java    |   5 +
 .../apache/nifi/toolkit/cli/api/Command.java    |   5 +
 .../nifi/toolkit/cli/api/CommandGroup.java      |   2 +-
 .../apache/nifi/toolkit/cli/api/Context.java    |   2 +
 .../apache/nifi/toolkit/cli/api/ResultType.java |  24 +++
 .../nifi/toolkit/cli/api/ResultWriter.java      |  65 ++++++
 .../cli/impl/client/nifi/FlowClient.java        |  10 +-
 .../cli/impl/client/nifi/ProcessGroupBox.java   | 149 +++++++++++++
 .../impl/client/nifi/ProcessGroupClient.java    |  11 +
 .../impl/client/nifi/impl/JerseyFlowClient.java |  64 +++---
 .../nifi/impl/JerseyProcessGroupClient.java     |  69 ++++++
 .../cli/impl/client/nifi/impl/PgBox.java        | 124 -----------
 .../cli/impl/command/AbstractCommand.java       |  73 +++----
 .../cli/impl/command/AbstractCommandGroup.java  |  25 ++-
 .../toolkit/cli/impl/command/CommandOption.java |   7 +-
 .../cli/impl/command/CommandProcessor.java      |  26 ++-
 .../toolkit/cli/impl/command/misc/Exit.java     |   5 +
 .../toolkit/cli/impl/command/misc/Help.java     |   5 +
 .../cli/impl/command/nifi/NiFiCommandGroup.java |   6 +
 .../cli/impl/command/nifi/flow/CurrentUser.java |  10 +-
 .../cli/impl/command/nifi/flow/GetRootId.java   |   5 +
 .../impl/command/nifi/pg/PGChangeVersion.java   |  15 +-
 .../impl/command/nifi/pg/PGGetAllVersions.java  |   9 +-
 .../cli/impl/command/nifi/pg/PGGetVars.java     |  10 +-
 .../cli/impl/command/nifi/pg/PGGetVersion.java  |  10 +-
 .../cli/impl/command/nifi/pg/PGImport.java      |  27 +--
 .../cli/impl/command/nifi/pg/PGList.java        |  84 ++++++++
 .../cli/impl/command/nifi/pg/PGSetVar.java      | 116 ++++++++++
 .../cli/impl/command/nifi/pg/PGStart.java       |   5 +
 .../cli/impl/command/nifi/pg/PGStop.java        |   5 +
 .../nifi/registry/CreateRegistryClient.java     |   5 +
 .../nifi/registry/GetRegistryClientId.java      | 100 +++++++++
 .../nifi/registry/ListRegistryClients.java      |  10 +-
 .../nifi/registry/UpdateRegistryClient.java     |   5 +
 .../registry/AbstractNiFiRegistryCommand.java   |  21 ++
 .../command/registry/bucket/CreateBucket.java   |   5 +
 .../command/registry/bucket/ListBuckets.java    |   9 +-
 .../impl/command/registry/flow/CreateFlow.java  |   5 +
 .../registry/flow/ExportFlowVersion.java        |  40 ++--
 .../registry/flow/ImportFlowVersion.java        |  31 +--
 .../command/registry/flow/ListFlowVersions.java |  13 +-
 .../impl/command/registry/flow/ListFlows.java   |  11 +-
 .../impl/command/registry/user/CurrentUser.java |  10 +-
 .../cli/impl/command/session/ClearSession.java  |   5 +
 .../cli/impl/command/session/GetVariable.java   |   5 +
 .../impl/command/session/RemoveVariable.java    |   5 +
 .../cli/impl/command/session/SetVariable.java   |   6 +
 .../cli/impl/command/session/ShowKeys.java      |   5 +
 .../cli/impl/command/session/ShowSession.java   |   5 +
 .../cli/impl/context/StandardContext.java       |  35 +++
 .../cli/impl/result/JsonResultWriter.java       | 111 ++++++++++
 .../cli/impl/result/SimpleResultWriter.java     | 211 +++++++++++++++++++
 .../toolkit/cli/impl/util/JacksonUtils.java     |  72 +++++++
 .../nifi/toolkit/cli/NiFiCLIMainRunner.java     |  11 +-
 .../nifi/toolkit/cli/TestCLICompleter.java      |  15 +-
 58 files changed, 1496 insertions(+), 305 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-assembly/LICENSE
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-assembly/LICENSE b/nifi-toolkit/nifi-toolkit-assembly/LICENSE
index 412bea3..a49345f 100644
--- a/nifi-toolkit/nifi-toolkit-assembly/LICENSE
+++ b/nifi-toolkit/nifi-toolkit-assembly/LICENSE
@@ -254,3 +254,40 @@ licenses.
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     THE SOFTWARE.
 
+  The binary distribution of this produce bundles 'JLine' under a BSD
+  style license.
+
+    Copyright (c) 2002-2017, the original author or authors.
+    All rights reserved.
+
+    http://www.opensource.org/licenses/bsd-license.php
+
+    Redistribution and use in source and binary forms, with or
+    without modification, are permitted provided that the following
+    conditions are met:
+
+    Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with
+    the distribution.
+
+    Neither the name of JLine nor the names of its contributors
+    may be used to endorse or promote products derived from this
+    software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+    BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+    EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+    OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/README.md
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/README.md b/nifi-toolkit/nifi-toolkit-cli/README.md
index 03953e9..ed3bdb1 100644
--- a/nifi-toolkit/nifi-toolkit-cli/README.md
+++ b/nifi-toolkit/nifi-toolkit-cli/README.md
@@ -88,4 +88,38 @@ will auto-complete the path being typed:
     #> session set nifi.props /tmp/
     dir1/   dir2/   dir3/
     
-    
\ No newline at end of file
+## Output
+
+All commands (except export-flow-version) support the ability to specify an <code>--outputType</code> argument, 
+or <code>-ot</code> for short.
+
+Currently the output type may be <code>simple</code> or <code>json</code>.
+
+The default output type in interactive mode is <code>simple</code>, 
+and the default output type in standalone mode is <code>json</code>.
+
+Example of simple output for list-buckets:
+
+    #> registry list-buckets -ot simple
+    
+    My Bucket - 3c7b7467-0012-4d8f-a918-6aa42b6b9d39
+
+Example of json output for list-buckets:
+
+    #> registry list-buckets -ot json
+    [ {
+      "identifier" : "3c7b7467-0012-4d8f-a918-6aa42b6b9d39",
+      "name" : "My Bucket",
+      "createdTimestamp" : 1516718733854,
+      "permissions" : {
+        "canRead" : true,
+        "canWrite" : true,
+        "canDelete" : true
+      },
+      "link" : {
+        "params" : {
+          "rel" : "self"
+        },
+        "href" : "buckets/3c7b7467-0012-4d8f-a918-6aa42b6b9d39"
+      }
+    } ]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/pom.xml b/nifi-toolkit/nifi-toolkit-cli/pom.xml
index 448e85b..2f9e18c 100644
--- a/nifi-toolkit/nifi-toolkit-cli/pom.xml
+++ b/nifi-toolkit/nifi-toolkit-cli/pom.xml
@@ -84,9 +84,5 @@
             <artifactId>commons-io</artifactId>
             <version>2.6</version>
         </dependency>
-        <dependency>
-            <groupId>com.jayway.jsonpath</groupId>
-            <artifactId>json-path</artifactId>
-        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
index 643fe03..95da2fc 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
@@ -23,6 +23,7 @@ import org.apache.nifi.toolkit.cli.api.ClientFactory;
 import org.apache.nifi.toolkit.cli.api.Command;
 import org.apache.nifi.toolkit.cli.api.CommandGroup;
 import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ResultType;
 import org.apache.nifi.toolkit.cli.api.Session;
 import org.apache.nifi.toolkit.cli.impl.client.NiFiClientFactory;
 import org.apache.nifi.toolkit.cli.impl.client.NiFiRegistryClientFactory;
@@ -30,6 +31,8 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.command.CommandFactory;
 import org.apache.nifi.toolkit.cli.impl.command.CommandProcessor;
 import org.apache.nifi.toolkit.cli.impl.context.StandardContext;
+import org.apache.nifi.toolkit.cli.impl.result.JsonResultWriter;
+import org.apache.nifi.toolkit.cli.impl.result.SimpleResultWriter;
 import org.apache.nifi.toolkit.cli.impl.session.InMemorySession;
 import org.apache.nifi.toolkit.cli.impl.session.PersistentSession;
 import org.jline.reader.Completer;
@@ -192,6 +195,8 @@ public class CLIMain {
                 .nifiClientFactory(niFiClientFactory)
                 .nifiRegistryClientFactory(nifiRegClientFactory)
                 .interactive(isInteractive)
+                .resultWriter(ResultType.SIMPLE, new SimpleResultWriter())
+                .resultWriter(ResultType.JSON, new JsonResultWriter())
                 .build();
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Command.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Command.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Command.java
index 93e06d0..09ba5c8 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Command.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Command.java
@@ -37,6 +37,11 @@ public interface Command {
     String getName();
 
     /**
+     * @return the description of the command to be printed in help messages
+     */
+    String getDescription();
+
+    /**
      * @return the CLI options of the command
      */
     Options getOptions();

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/CommandGroup.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/CommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/CommandGroup.java
index bdc4eae..7104cb6 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/CommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/CommandGroup.java
@@ -43,6 +43,6 @@ public interface CommandGroup {
     /**
      * Prints the usage for this group.
      */
-    void printUsage();
+    void printUsage(boolean verbose);
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Context.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Context.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Context.java
index 3053f78..14b5186 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Context.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/Context.java
@@ -36,4 +36,6 @@ public interface Context {
 
     boolean isInteractive();
 
+    ResultWriter getResultWriter(ResultType resultType);
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultType.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultType.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultType.java
new file mode 100644
index 0000000..9a02eb4
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultType.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.toolkit.cli.api;
+
+public enum ResultType {
+
+    SIMPLE,
+    JSON;
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultWriter.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultWriter.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultWriter.java
new file mode 100644
index 0000000..b7b0741
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResultWriter.java
@@ -0,0 +1,65 @@
+/*
+ * 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.nifi.toolkit.cli.api;
+
+import org.apache.nifi.registry.authorization.CurrentUser;
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
+import org.apache.nifi.web.api.entity.CurrentUserEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupEntity;
+import org.apache.nifi.web.api.entity.RegistryClientsEntity;
+import org.apache.nifi.web.api.entity.VariableRegistryEntity;
+import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
+import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.List;
+
+/**
+ * Responsible for writing entities to the given stream.
+ */
+public interface ResultWriter {
+
+    void writeBuckets(List<Bucket> buckets, PrintStream output) throws IOException;
+
+    void writeBucket(Bucket bucket, PrintStream output) throws IOException;
+
+    void writeFlows(List<VersionedFlow> versionedFlows, PrintStream output) throws IOException;
+
+    void writeFlow(VersionedFlow versionedFlow, PrintStream output) throws IOException;
+
+    void writeSnapshotMetadata(List<VersionedFlowSnapshotMetadata> versions, PrintStream output) throws IOException;
+
+    void writeSnapshotMetadata(VersionedFlowSnapshotMetadata version, PrintStream output) throws IOException;
+
+    void writeRegistryClients(RegistryClientsEntity clientsEntity, PrintStream output) throws IOException;
+
+    void writeVariables(VariableRegistryEntity variableRegistryEntity, PrintStream output) throws IOException;
+
+    void writeSnapshotMetadata(VersionedFlowSnapshotMetadataSetEntity versionedFlowSnapshotMetadataSetEntity, PrintStream output) throws IOException;
+
+    void writeVersionControlInfo(VersionControlInformationEntity versionControlInformationEntity, PrintStream output) throws IOException;
+
+    void writeProcessGroups(List<ProcessGroupEntity> processGroupEntities, PrintStream output) throws IOException;
+
+    void writeCurrentUser(CurrentUserEntity currentUserEntity, PrintStream output) throws IOException;
+
+    void writeCurrentUser(CurrentUser currentUser, PrintStream output) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
index b27f0ad..5e56cd3 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
@@ -16,7 +16,6 @@
  */
 package org.apache.nifi.toolkit.cli.impl.client.nifi;
 
-import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.PgBox;
 import org.apache.nifi.web.api.entity.CurrentUserEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
@@ -52,12 +51,11 @@ public interface FlowClient {
     /**
      * Suggest a location for the new process group on a canvas, within a given process group.
      * Will locate to the right and then bottom and offset for the size of the PG box.
-     * @param parentId
-     * @return
-     * @throws NiFiClientException
-     * @throws IOException
+     *
+     * @param parentId the id of the parent process group where the new process group will be located
+     * @return the process group box where a new process group can be placed
      */
-    PgBox getSuggestedProcessGroupCoordinates(String parentId) throws NiFiClientException, IOException;
+    ProcessGroupBox getSuggestedProcessGroupCoordinates(String parentId) throws NiFiClientException, IOException;
 
     /**
      * Schedules the components of a process group.

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupBox.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupBox.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupBox.java
new file mode 100644
index 0000000..ce1cac7
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupBox.java
@@ -0,0 +1,149 @@
+/*
+ * 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.nifi.toolkit.cli.impl.client.nifi;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Represents the bounding box the Processing Group and some placement logic.
+ */
+public class ProcessGroupBox implements Comparable<ProcessGroupBox> {
+    // values as specified in the nf-process-group.js file
+    public static final int PG_SIZE_WIDTH = 380;
+    public static final int PG_SIZE_HEIGHT = 172;
+    // minimum whitespace between PG elements for auto-layout
+    public static final int PG_SPACING = 50;
+
+    private final int x;
+    private final int y;
+
+    public static final ProcessGroupBox CANVAS_CENTER = new ProcessGroupBox(0, 0);
+
+    public ProcessGroupBox(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public int getX() {
+        return x;
+    }
+
+    public int getY() {
+        return y;
+    }
+
+    /**
+     * @return distance from a (0, 0) point.
+     */
+    public int distance() {
+        // a simplified distance formula because the other coord is (0, 0)
+        return (int) Math.hypot(x, y);
+    }
+
+
+    public boolean intersects(ProcessGroupBox other) {
+        // adapted for java.awt Rectangle, we don't want to import it
+        // assume everything to be of the PG size for simplicity
+        int tw = PG_SIZE_WIDTH;
+        int th = PG_SIZE_HEIGHT;
+        // 2nd pg box includes spacers
+        int rw = PG_SIZE_WIDTH;
+        int rh = PG_SIZE_HEIGHT;
+        if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
+            return false;
+        }
+        double tx = this.x;
+        double ty = this.y;
+        double rx = other.x;
+        double ry = other.y;
+        rw += rx;
+        rh += ry;
+        tw += tx;
+        th += ty;
+        //      overflow || intersect
+        return ((rw < rx || rw > tx)
+                && (rh < ry || rh > ty)
+                && (tw < tx || tw > rx)
+                && (th < ty || th > ry));
+    }
+
+
+    public ProcessGroupBox findFreeSpace(List<ProcessGroupBox> allCoords) {
+        // sort by distance to (0.0)
+        List<ProcessGroupBox> byClosest = allCoords.stream().sorted().collect(Collectors.toList());
+
+        // search to the right
+        List<ProcessGroupBox> freeSpots = byClosest.stream().filter(other ->
+                byClosest.stream().noneMatch(other.right()::intersects)
+        ).map(ProcessGroupBox::right).collect(Collectors.toList()); // save a 'transformed' spot 'to the right'
+
+        // search down
+        freeSpots.addAll(byClosest.stream().filter(other ->
+                byClosest.stream().noneMatch(other.down()::intersects)
+        ).map(ProcessGroupBox::down).collect(Collectors.toList()));
+
+        // search left
+        freeSpots.addAll(byClosest.stream().filter(other ->
+                byClosest.stream().noneMatch(other.left()::intersects)
+        ).map(ProcessGroupBox::left).collect(Collectors.toList()));
+
+        // search above
+        freeSpots.addAll(byClosest.stream().filter(other ->
+                byClosest.stream().noneMatch(other.up()::intersects)
+        ).map(ProcessGroupBox::up).collect(Collectors.toList()));
+
+        // return a free spot closest to (0, 0)
+        return freeSpots.stream().sorted().findFirst().orElse(CANVAS_CENTER);
+    }
+
+    public ProcessGroupBox right() {
+        return new ProcessGroupBox(this.x + PG_SIZE_WIDTH + PG_SPACING, this.y);
+    }
+
+    public ProcessGroupBox down() {
+        return new ProcessGroupBox(this.x, this.y + PG_SIZE_HEIGHT + PG_SPACING);
+    }
+
+    public ProcessGroupBox up() {
+        return new ProcessGroupBox(this.x, this.y - PG_SPACING - PG_SIZE_HEIGHT);
+    }
+
+    public ProcessGroupBox left() {
+        return new ProcessGroupBox(this.x - PG_SPACING - PG_SIZE_WIDTH, this.y);
+    }
+
+    @Override
+    public int compareTo(ProcessGroupBox other) {
+        return this.distance() - other.distance();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ProcessGroupBox pgBox = (ProcessGroupBox) o;
+        return Double.compare(pgBox.x, x) == 0
+                && Double.compare(pgBox.y, y) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(x, y);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java
index 037c310..299a3fd 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java
@@ -18,6 +18,7 @@ package org.apache.nifi.toolkit.cli.impl.client.nifi;
 
 import org.apache.nifi.web.api.entity.ProcessGroupEntity;
 import org.apache.nifi.web.api.entity.VariableRegistryEntity;
+import org.apache.nifi.web.api.entity.VariableRegistryUpdateRequestEntity;
 
 import java.io.IOException;
 
@@ -35,4 +36,14 @@ public interface ProcessGroupClient {
 
     VariableRegistryEntity getVariables(String processGroupId) throws NiFiClientException, IOException;
 
+    VariableRegistryUpdateRequestEntity updateVariableRegistry(
+            String processGroupId, VariableRegistryEntity variableRegistryEntity)
+            throws NiFiClientException, IOException;
+
+    VariableRegistryUpdateRequestEntity getVariableRegistryUpdateRequest(String processGroupdId, String requestId)
+            throws NiFiClientException, IOException;
+
+    VariableRegistryUpdateRequestEntity deleteVariableRegistryUpdateRequest(String processGroupdId, String requestId)
+            throws NiFiClientException, IOException;
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
index f762fab..26c082a 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
@@ -16,11 +16,14 @@
  */
 package org.apache.nifi.toolkit.cli.impl.client.nifi.impl;
 
-import com.jayway.jsonpath.JsonPath;
-import net.minidev.json.JSONArray;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupBox;
+import org.apache.nifi.web.api.dto.PositionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO;
+import org.apache.nifi.web.api.entity.ComponentEntity;
 import org.apache.nifi.web.api.entity.CurrentUserEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
@@ -29,12 +32,12 @@ import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -85,38 +88,35 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient
     }
 
     @Override
-    public PgBox getSuggestedProcessGroupCoordinates(String parentId) throws NiFiClientException, IOException {
+    public ProcessGroupBox getSuggestedProcessGroupCoordinates(final String parentId) throws NiFiClientException, IOException {
         if (StringUtils.isBlank(parentId)) {
             throw new IllegalArgumentException("Process group id cannot be null");
         }
 
-        return executeAction("Error retrieving process group flow", () -> {
-            final WebTarget target = flowTarget
-                    .path("process-groups/{id}")
-                    .resolveTemplate("id", parentId);
-
-            Response response = getRequestBuilder(target).get();
-
-            String json = response.readEntity(String.class);
-
-            JSONArray jsonArray = JsonPath.compile("$..position").read(json);
-
-            if (jsonArray.isEmpty()) {
-                // it's an empty nifi canvas, nice to align
-                return PgBox.CANVAS_CENTER;
-            }
-
-            List<PgBox> coords = new HashSet<>(jsonArray) // de-dup the initial set
-                    .stream().map(Map.class::cast)
-                    .map(m -> new PgBox(Double.valueOf(m.get("x").toString()).intValue(),
-                                         Double.valueOf(m.get("y").toString()).intValue()))
-                    .collect(Collectors.toList());
-
-            PgBox freeSpot = coords.get(0).findFreeSpace(coords);
-
-            return freeSpot;
-        });
-
+        final ProcessGroupFlowEntity processGroup = getProcessGroup(parentId);
+        final ProcessGroupFlowDTO processGroupFlowDTO = processGroup.getProcessGroupFlow();
+        final FlowDTO flowDTO = processGroupFlowDTO.getFlow();
+
+        final List<ComponentEntity> pgComponents = new ArrayList<>();
+        pgComponents.addAll(flowDTO.getProcessGroups());
+        pgComponents.addAll(flowDTO.getProcessors());
+        pgComponents.addAll(flowDTO.getRemoteProcessGroups());
+        pgComponents.addAll(flowDTO.getConnections());
+        pgComponents.addAll(flowDTO.getFunnels());
+        pgComponents.addAll(flowDTO.getInputPorts());
+        pgComponents.addAll(flowDTO.getOutputPorts());
+        pgComponents.addAll(flowDTO.getLabels());
+
+        final Set<PositionDTO> positions = pgComponents.stream()
+                .map(c -> c.getPosition())
+                .collect(Collectors.toSet());
+
+        final List<ProcessGroupBox> coords = positions.stream()
+                .map(p -> new ProcessGroupBox(p.getX().intValue(), p.getY().intValue()))
+                .collect(Collectors.toList());
+
+        final ProcessGroupBox freeSpot = coords.get(0).findFreeSpace(coords);
+        return freeSpot;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java
index 65bac4f..387f9c9 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java
@@ -21,6 +21,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
 import org.apache.nifi.web.api.entity.ProcessGroupEntity;
 import org.apache.nifi.web.api.entity.VariableRegistryEntity;
+import org.apache.nifi.web.api.entity.VariableRegistryUpdateRequestEntity;
 
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
@@ -118,4 +119,72 @@ public class JerseyProcessGroupClient extends AbstractJerseyClient implements Pr
             return getRequestBuilder(target).get(VariableRegistryEntity.class);
         });
     }
+
+    @Override
+    public VariableRegistryUpdateRequestEntity updateVariableRegistry(
+            final String processGroupId, final VariableRegistryEntity variableRegistryEntity)
+            throws NiFiClientException, IOException {
+
+        if (StringUtils.isBlank(processGroupId)) {
+            throw new IllegalArgumentException("Process group id cannot be null or blank");
+        }
+
+        if (variableRegistryEntity == null) {
+            throw new IllegalArgumentException("Variable registry entity cannot be null");
+        }
+
+        return executeAction("Error getting variable registry update request", () -> {
+            final WebTarget target = processGroupsTarget
+                    .path("{processGroupId}/variable-registry/update-requests")
+                    .resolveTemplate("processGroupId", processGroupId);
+
+            return getRequestBuilder(target).post(
+                    Entity.entity(variableRegistryEntity, MediaType.APPLICATION_JSON_TYPE),
+                    VariableRegistryUpdateRequestEntity.class
+            );
+        });
+    }
+
+    @Override
+    public VariableRegistryUpdateRequestEntity getVariableRegistryUpdateRequest(
+            final String processGroupId, final String requestId) throws NiFiClientException, IOException {
+
+        if (StringUtils.isBlank(processGroupId)) {
+            throw new IllegalArgumentException("Process group id cannot be null or blank");
+        }
+
+        if (StringUtils.isBlank(requestId)) {
+            throw new IllegalArgumentException("Request id cannot be null or blank");
+        }
+
+        return executeAction("Error getting variable registry update request", () -> {
+            final WebTarget target = processGroupsTarget
+                    .path("{processGroupId}/variable-registry/update-requests/{updateId}")
+                    .resolveTemplate("processGroupId", processGroupId)
+                    .resolveTemplate("updateId", requestId);
+
+            return getRequestBuilder(target).get(VariableRegistryUpdateRequestEntity.class);
+        });
+    }
+
+    @Override
+    public VariableRegistryUpdateRequestEntity deleteVariableRegistryUpdateRequest(
+            final String processGroupId, final String requestId) throws NiFiClientException, IOException {
+        if (StringUtils.isBlank(processGroupId)) {
+            throw new IllegalArgumentException("Process group id cannot be null or blank");
+        }
+
+        if (StringUtils.isBlank(requestId)) {
+            throw new IllegalArgumentException("Request id cannot be null or blank");
+        }
+
+        return executeAction("Error getting variable registry update request", () -> {
+            final WebTarget target = processGroupsTarget
+                    .path("{processGroupId}/variable-registry/update-requests/{updateId}")
+                    .resolveTemplate("processGroupId", processGroupId)
+                    .resolveTemplate("updateId", requestId);
+
+            return getRequestBuilder(target).delete(VariableRegistryUpdateRequestEntity.class);
+        });
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/PgBox.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/PgBox.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/PgBox.java
deleted file mode 100644
index 5c4f575..0000000
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/PgBox.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.apache.nifi.toolkit.cli.impl.client.nifi.impl;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * Represents the bounding box the Processing Group and some placement logic.
- */
-public class PgBox implements Comparable<PgBox> {
-    // values as specified in the nf-process-group.js file
-    public static final int PG_SIZE_WIDTH = 380;
-    public static final int PG_SIZE_HEIGHT = 172;
-    // minimum whitespace between PG elements for auto-layout
-    public static final int PG_SPACING = 50;
-    public int x;
-    public int y;
-
-    public static final PgBox CANVAS_CENTER = new PgBox(0, 0);
-
-    public PgBox(int x, int y) {
-        this.x = x;
-        this.y = y;
-    }
-
-    /**
-     * @return distance from a (0, 0) point.
-     */
-    public int distance() {
-        // a simplified distance formula because the other coord is (0, 0)
-        return (int) Math.hypot(x, y);
-    }
-
-
-    public boolean intersects(PgBox other) {
-        // adapted for java.awt Rectangle, we don't want to import it
-        // assume everything to be of the PG size for simplicity
-        int tw = PG_SIZE_WIDTH;
-        int th = PG_SIZE_HEIGHT;
-        // 2nd pg box includes spacers
-        int rw = PG_SIZE_WIDTH;
-        int rh = PG_SIZE_HEIGHT;
-        if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
-            return false;
-        }
-        double tx = this.x;
-        double ty = this.y;
-        double rx = other.x;
-        double ry = other.y;
-        rw += rx;
-        rh += ry;
-        tw += tx;
-        th += ty;
-        //      overflow || intersect
-        return ((rw < rx || rw > tx) &&
-                (rh < ry || rh > ty) &&
-                (tw < tx || tw > rx) &&
-                (th < ty || th > ry));
-    }
-
-
-    public PgBox findFreeSpace(List<PgBox> allCoords) {
-        // sort by distance to (0.0)
-        List<PgBox> byClosest = allCoords.stream().sorted().collect(Collectors.toList());
-
-        // search to the right
-        List<PgBox> freeSpots = byClosest.stream().filter(other ->
-                byClosest.stream().noneMatch(other.right()::intersects)
-        ).map(PgBox::right).collect(Collectors.toList()); // save a 'transformed' spot 'to the right'
-
-        // search down
-        freeSpots.addAll(byClosest.stream().filter(other ->
-                byClosest.stream().noneMatch(other.down()::intersects)
-        ).map(PgBox::down).collect(Collectors.toList()));
-
-        // search left
-        freeSpots.addAll(byClosest.stream().filter(other ->
-                byClosest.stream().noneMatch(other.left()::intersects)
-        ).map(PgBox::left).collect(Collectors.toList()));
-
-        // search above
-        freeSpots.addAll(byClosest.stream().filter(other ->
-                byClosest.stream().noneMatch(other.up()::intersects)
-        ).map(PgBox::up).collect(Collectors.toList()));
-
-        // return a free spot closest to (0, 0)
-        return freeSpots.stream().sorted().findFirst().orElse(CANVAS_CENTER);
-    }
-
-    public PgBox right() {
-        return new PgBox(this.x + PG_SIZE_WIDTH + PG_SPACING, this.y);
-    }
-
-    public PgBox down() {
-        return new PgBox(this.x, this.y + PG_SIZE_HEIGHT + PG_SPACING);
-    }
-
-    public PgBox up() {
-        return new PgBox(this.x, this.y - PG_SPACING - PG_SIZE_HEIGHT);
-    }
-
-    public PgBox left() {
-        return new PgBox(this.x - PG_SPACING - PG_SIZE_WIDTH, this.y);
-    }
-
-    @Override
-    public int compareTo(PgBox other) {
-        return this.distance() - other.distance();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        PgBox pgBox = (PgBox) o;
-        return Double.compare(pgBox.x, x) == 0 &&
-                Double.compare(pgBox.y, y) == 0;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(x, y);
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
index 0b82947..05cc27d 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
@@ -16,11 +16,6 @@
  */
 package org.apache.nifi.toolkit.cli.impl.command;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectWriter;
-import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.MissingOptionException;
 import org.apache.commons.cli.Option;
@@ -29,10 +24,9 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.Validate;
 import org.apache.nifi.toolkit.cli.api.Command;
 import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.api.ResultWriter;
 
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.util.Properties;
@@ -42,16 +36,6 @@ import java.util.Properties;
  */
 public abstract class AbstractCommand implements Command {
 
-    protected static final ObjectMapper MAPPER = new ObjectMapper();
-    static {
-        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
-        MAPPER.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));
-        MAPPER.setAnnotationIntrospector(new JaxbAnnotationIntrospector(MAPPER.getTypeFactory()));
-        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
-    }
-
-    protected static final ObjectWriter OBJECT_WRITER = MAPPER.writerWithDefaultPrettyPrinter();
-
     private final String name;
     private final Options options;
 
@@ -78,6 +62,7 @@ public abstract class AbstractCommand implements Command {
 
         this.options.addOption(CommandOption.PROXIED_ENTITY.createOption());
 
+        this.options.addOption(CommandOption.OUTPUT_TYPE.createOption());
         this.options.addOption(CommandOption.VERBOSE.createOption());
         this.options.addOption(CommandOption.HELP.createOption());
     }
@@ -124,8 +109,13 @@ public abstract class AbstractCommand implements Command {
 
         final PrintWriter printWriter = new PrintWriter(output);
 
+        final int width = 160;
         final HelpFormatter hf = new HelpFormatter();
-        hf.setWidth(160);
+        hf.setWidth(width);
+
+        hf.printWrapped(printWriter, width, getDescription());
+        hf.printWrapped(printWriter, width, "");
+
         hf.printHelp(printWriter, hf.getWidth(), getName(), null, getOptions(),
                 hf.getLeftPadding(), hf.getDescPadding(), null, false);
 
@@ -146,37 +136,24 @@ public abstract class AbstractCommand implements Command {
         output.println();
     }
 
-    protected void writeResult(final Properties properties, final Object result) throws IOException {
-        if (properties.containsKey(CommandOption.OUTPUT_FILE.getLongName())) {
-            final String outputFile = properties.getProperty(CommandOption.OUTPUT_FILE.getLongName());
-            try (final OutputStream resultOut = new FileOutputStream(outputFile)) {
-                OBJECT_WRITER.writeValue(resultOut, result);
-            }
+    protected ResultWriter getResultWriter(final Properties properties) {
+        final ResultType resultType = getResultType(properties);
+        return context.getResultWriter(resultType);
+    }
+
+    protected ResultType getResultType(final Properties properties) {
+        final ResultType resultType;
+        if (properties.containsKey(CommandOption.OUTPUT_TYPE.getLongName())) {
+            final String outputTypeValue = properties.getProperty(CommandOption.OUTPUT_TYPE.getLongName());
+            resultType = ResultType.valueOf(outputTypeValue.toUpperCase());
         } else {
-            OBJECT_WRITER.writeValue(new OutputStream() {
-                @Override
-                public void write(byte[] b) throws IOException {
-                    output.write(b);
-                }
-
-                @Override
-                public void write(byte[] b, int off, int len) throws IOException {
-                    output.write(b, off, len);
-                }
-
-                @Override
-                public void write(int b) throws IOException {
-                    output.write(b);
-                }
-
-                @Override
-                public void close() throws IOException {
-                    // DON'T close the output stream here
-                    output.flush();
-                }
-            }, result);
+            if (getContext().isInteractive()) {
+                resultType = ResultType.SIMPLE;
+            } else {
+                resultType = ResultType.JSON;
+            }
         }
-
+        return resultType;
     }
 
     protected String getArg(final Properties properties, final CommandOption option) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java
index f597365..531c025 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java
@@ -16,12 +16,14 @@
  */
 package org.apache.nifi.toolkit.cli.impl.command;
 
+import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.lang3.Validate;
 import org.apache.nifi.toolkit.cli.api.Command;
 import org.apache.nifi.toolkit.cli.api.CommandGroup;
 import org.apache.nifi.toolkit.cli.api.Context;
 
 import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.List;
 
@@ -65,8 +67,27 @@ public abstract class AbstractCommandGroup implements CommandGroup {
     }
 
     @Override
-    public void printUsage() {
-        commands.stream().forEach(c -> output.println("\t" + getName() + " " + c.getName()));
+    public void printUsage(final boolean verbose) {
+        if (verbose) {
+            final PrintWriter printWriter = new PrintWriter(output);
+
+            final int width = 80;
+            final HelpFormatter hf = new HelpFormatter();
+            hf.setWidth(width);
+
+            commands.stream().forEach(c -> {
+                hf.printWrapped(printWriter, width, "-------------------------------------------------------------------------------");
+                hf.printWrapped(printWriter, width, "COMMAND: " + getName() + " " + c.getName());
+                hf.printWrapped(printWriter, width, "");
+                hf.printWrapped(printWriter, width, "- " + c.getDescription());
+                hf.printWrapped(printWriter, width, "");
+            });
+
+            printWriter.flush();
+
+        } else {
+            commands.stream().forEach(c -> output.println("\t" + getName() + " " + c.getName()));
+        }
         output.flush();
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
index 0b14ee3..7f890cb 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
@@ -50,10 +50,8 @@ public enum CommandOption {
     // NiFi - PGs
     PG_ID("pgid", "processGroupId", "The id of a process group", true),
     PG_NAME("pgn", "processGroupName", "The name of a process group", true),
-
-    // NiFi - Pos
-    POS_X("px", "posX", "The X coordinate", true),
-    POS_Y("py", "posY", "The Y coordinate", true),
+    PG_VAR_NAME("var", "varName", "The name of a variable", true),
+    PG_VAR_VALUE("val", "varValue", "The value of a variable", true),
 
     // Security related
     KEYSTORE("ks", "keystore", "A keystore to use for TLS/SSL connections", true),
@@ -67,6 +65,7 @@ public enum CommandOption {
     PROTOCOL("pro", "protocol", "The security protocol to use, such as TLSv.1.2", true),
 
     // Miscellaneous
+    OUTPUT_TYPE("ot", "outputType", "The type of output to produce (json or simple)", true),
     VERBOSE("verbose", "verbose", "Indicates that verbose output should be provided", false),
     HELP("h", "help", "Help", false)
     ;

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
index b87b80a..6cf3114 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
@@ -52,6 +52,10 @@ public class CommandProcessor {
     }
 
     public void printBasicUsage(String errorMessage) {
+        printBasicUsage(errorMessage, false);
+    }
+
+    public void printBasicUsage(String errorMessage, boolean verbose) {
         out.println();
 
         if (errorMessage != null) {
@@ -62,7 +66,8 @@ public class CommandProcessor {
         out.println("commands:");
         out.println();
 
-        commandGroups.entrySet().stream().forEach(e -> e.getValue().printUsage());
+        commandGroups.entrySet().stream().forEach(e -> e.getValue().printUsage(verbose));
+        out.println("-------------------------------------------------------------------------------");
         topLevelCommands.keySet().stream().forEach(k -> out.println("\t" + k));
         out.println();
     }
@@ -81,12 +86,21 @@ public class CommandProcessor {
     }
 
     public void process(String[] args) {
-        if (args == null || args.length == 0
-                || (args.length == 1 && CommandOption.HELP.getLongName().equalsIgnoreCase(args[0]))) {
+        if (args == null || args.length == 0) {
             printBasicUsage(null);
             return;
         }
 
+        if (CommandOption.HELP.getLongName().equalsIgnoreCase(args[0])) {
+            if (args.length == 2 && "-v".equalsIgnoreCase(args[1])) {
+                printBasicUsage(null, true);
+                return;
+            } else {
+                printBasicUsage(null);
+                return;
+            }
+        }
+
         final String commandStr = args[0];
         if (topLevelCommands.containsKey(commandStr)) {
             processTopLevelCommand(commandStr, args);
@@ -139,7 +153,11 @@ public class CommandProcessor {
 
         final String commandStr = args[1];
         final CommandGroup commandGroup = commandGroups.get(commandGroupStr);
-        final Command command = commandGroup.getCommands().stream().filter(c -> c.getName().equals(commandStr)).findFirst().orElse(null);
+
+        final Command command = commandGroup.getCommands().stream()
+                .filter(c -> c.getName().equals(commandStr))
+                .findFirst()
+                .orElse(null);
 
         if (command == null) {
             printBasicUsage("Unknown command '" + commandGroupStr + " " + commandStr + "'");

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java
index b10d17a..974f7f0 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java
@@ -38,6 +38,11 @@ public class Exit implements Command {
     }
 
     @Override
+    public String getDescription() {
+        return "";
+    }
+
+    @Override
     public Options getOptions() {
         return new Options();
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java
index cf3c558..0544089 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java
@@ -38,6 +38,11 @@ public class Help implements Command {
     }
 
     @Override
+    public String getDescription() {
+        return "";
+    }
+
+    @Override
     public Options getOptions() {
         return new Options();
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
index 0c6dd8e..69e79bc 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
@@ -25,9 +25,12 @@ import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetAllVersions;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVars;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVersion;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGImport;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGList;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGSetVar;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStart;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStop;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.CreateRegistryClient;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.GetRegistryClientId;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListRegistryClients;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.UpdateRegistryClient;
 
@@ -53,13 +56,16 @@ public class NiFiCommandGroup extends AbstractCommandGroup {
         commands.add(new ListRegistryClients());
         commands.add(new CreateRegistryClient());
         commands.add(new UpdateRegistryClient());
+        commands.add(new GetRegistryClientId());
         commands.add(new PGImport());
         commands.add(new PGStart());
         commands.add(new PGStop());
         commands.add(new PGGetVars());
+        commands.add(new PGSetVar());
         commands.add(new PGGetVersion());
         commands.add(new PGChangeVersion());
         commands.add(new PGGetAllVersions());
+        commands.add(new PGList());
         return new ArrayList<>(commands);
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CurrentUser.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CurrentUser.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CurrentUser.java
index 4bff9ed..4c09510 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CurrentUser.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CurrentUser.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.toolkit.cli.impl.command.nifi.flow;
 
+import org.apache.nifi.toolkit.cli.api.ResultWriter;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
@@ -34,9 +35,16 @@ public class CurrentUser extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Returns information about the user accessing NiFi. " +
+                "This provides a way to test if the CLI is accessing NiFi as the expected user.";
+    }
+
+    @Override
     protected void doExecute(NiFiClient client, Properties properties)
             throws NiFiClientException, IOException {
         final FlowClient flowClient = client.getFlowClient();
-        writeResult(properties, flowClient.getCurrentUser());
+        final ResultWriter resultWriter = getResultWriter(properties);
+        resultWriter.writeCurrentUser(flowClient.getCurrentUser(), getContext().getOutput());
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java
index bb771d8..bf1fb75 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java
@@ -34,6 +34,11 @@ public class GetRootId extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Returns the id of the root process group of the given NiFi instance.";
+    }
+
+    @Override
     protected void doExecute(final NiFiClient client, final Properties properties)
             throws NiFiClientException, IOException {
         final FlowClient flowClient = client.getFlowClient();

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGChangeVersion.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGChangeVersion.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGChangeVersion.java
index ddaaa26..87d7845 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGChangeVersion.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGChangeVersion.java
@@ -44,6 +44,13 @@ public class PGChangeVersion extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Changes the version for a version controlled process group. " +
+                "This can be used to upgrade to a new version, or revert to a previous version. " +
+                "If no version is specified, the latest version will be used.";
+    }
+
+    @Override
     protected void doInitialize(final Context context) {
         addOption(CommandOption.PG_ID.createOption());
         addOption(CommandOption.FLOW_VERSION.createOption());
@@ -77,8 +84,11 @@ public class PGChangeVersion extends AbstractNiFiCommand {
         // update the version in the existing DTO to the new version so we can submit it back
         existingVersionControlDTO.setVersion(newVersion);
 
+        // initiate the version change which creates an update request that must be checked for completion
         final VersionedFlowUpdateRequestEntity initialUpdateRequest = versionsClient.updateVersionControlInfo(pgId, existingVersionControlInfo);
 
+        // poll the update request for up to 30 seconds to see if it has completed
+        // if it doesn't complete then an exception will be thrown, but in either case the request will be deleted
         final String updateRequestId = initialUpdateRequest.getRequest().getRequestId();
         try {
             boolean completed = false;
@@ -89,7 +99,10 @@ public class PGChangeVersion extends AbstractNiFiCommand {
                     break;
                 } else {
                     try {
-                        Thread.sleep(1000);
+                        if (getContext().isInteractive()) {
+                            println("Waiting for update request to complete...");
+                        }
+                        Thread.sleep(2000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetAllVersions.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetAllVersions.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetAllVersions.java
index c9895f0..b26dffd 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetAllVersions.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetAllVersions.java
@@ -19,6 +19,7 @@ package org.apache.nifi.toolkit.cli.impl.command.nifi.pg;
 import org.apache.commons.cli.MissingOptionException;
 import org.apache.nifi.toolkit.cli.api.CommandException;
 import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ResultWriter;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
@@ -40,6 +41,10 @@ public class PGGetAllVersions extends AbstractNiFiCommand {
     public PGGetAllVersions() {
         super("pg-get-all-versions");
     }
+    @Override
+    public String getDescription() {
+        return "Returns all of the available versions for a version controlled process group.";
+    }
 
     @Override
     protected void doInitialize(final Context context) {
@@ -70,6 +75,8 @@ public class PGGetAllVersions extends AbstractNiFiCommand {
             throw new NiFiClientException("No versions available");
         }
 
-        writeResult(properties, versions);
+        final ResultWriter resultWriter = getResultWriter(properties);
+        resultWriter.writeSnapshotMetadata(versions, getContext().getOutput());
     }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVars.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVars.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVars.java
index 50c4bcf..6c5072c 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVars.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVars.java
@@ -19,6 +19,7 @@ package org.apache.nifi.toolkit.cli.impl.command.nifi.pg;
 import org.apache.commons.cli.MissingOptionException;
 import org.apache.nifi.toolkit.cli.api.CommandException;
 import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ResultWriter;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
@@ -39,6 +40,11 @@ public class PGGetVars extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Returns the variable registry for a given process group.";
+    }
+
+    @Override
     protected void doInitialize(final Context context) {
         addOption(CommandOption.PG_ID.createOption());
     }
@@ -49,6 +55,8 @@ public class PGGetVars extends AbstractNiFiCommand {
         final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
         final ProcessGroupClient pgClient = client.getProcessGroupClient();
         final VariableRegistryEntity varEntity = pgClient.getVariables(pgId);
-        writeResult(properties, varEntity);
+
+        final ResultWriter resultWriter = getResultWriter(properties);
+        resultWriter.writeVariables(varEntity, getContext().getOutput());
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVersion.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVersion.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVersion.java
index d180d52..855cb9e 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVersion.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetVersion.java
@@ -19,6 +19,7 @@ package org.apache.nifi.toolkit.cli.impl.command.nifi.pg;
 import org.apache.commons.cli.MissingOptionException;
 import org.apache.nifi.toolkit.cli.api.CommandException;
 import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ResultWriter;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
 import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
@@ -38,6 +39,11 @@ public class PGGetVersion extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Returns the current version information for a version controlled process group.";
+    }
+
+    @Override
     protected void doInitialize(final Context context) {
         addOption(CommandOption.PG_ID.createOption());
     }
@@ -50,7 +56,9 @@ public class PGGetVersion extends AbstractNiFiCommand {
         if (entity.getVersionControlInformation() == null) {
             throw new NiFiClientException("Process group is not under version control");
         }
-        writeResult(properties, entity);
+
+        final ResultWriter resultWriter = getResultWriter(properties);
+        resultWriter.writeVersionControlInfo(entity, getContext().getOutput());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java
index 77958c8..d3a3866 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java
@@ -22,8 +22,8 @@ import org.apache.nifi.toolkit.cli.api.Context;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupBox;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
-import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.PgBox;
 import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
 import org.apache.nifi.web.api.dto.PositionDTO;
@@ -44,14 +44,18 @@ public class PGImport extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Creates a new process group by importing a versioned flow from a registry. If no process group id is " +
+                "specified, then the created process group will be placed in the root group.";
+    }
+
+    @Override
     protected void doInitialize(Context context) {
         addOption(CommandOption.PG_ID.createOption());
         addOption(CommandOption.REGISTRY_CLIENT_ID.createOption());
         addOption(CommandOption.BUCKET_ID.createOption());
         addOption(CommandOption.FLOW_ID.createOption());
         addOption(CommandOption.FLOW_VERSION.createOption());
-        addOption(CommandOption.POS_X.createOption());
-        addOption(CommandOption.POS_Y.createOption());
     }
 
     @Override
@@ -63,17 +67,6 @@ public class PGImport extends AbstractNiFiCommand {
         final String flowId = getRequiredArg(properties, CommandOption.FLOW_ID);
         final Integer flowVersion = getRequiredIntArg(properties, CommandOption.FLOW_VERSION);
 
-        // TODO - do we actually want the client to deal with X/Y coordinates? drop the args from API?
-        Integer posX = getIntArg(properties, CommandOption.POS_X);
-        if (posX == null) {
-            posX = 0;
-        }
-
-        Integer posY = getIntArg(properties, CommandOption.POS_Y);
-        if (posY == null) {
-            posY = 0;
-        }
-
         // get the optional id of the parent PG, otherwise fallback to the root group
         String parentPgId = getArg(properties, CommandOption.PG_ID);
         if (StringUtils.isBlank(parentPgId)) {
@@ -87,11 +80,11 @@ public class PGImport extends AbstractNiFiCommand {
         versionControlInfo.setFlowId(flowId);
         versionControlInfo.setVersion(flowVersion);
 
-        PgBox pgBox = client.getFlowClient().getSuggestedProcessGroupCoordinates(parentPgId);
+        final ProcessGroupBox pgBox = client.getFlowClient().getSuggestedProcessGroupCoordinates(parentPgId);
 
         final PositionDTO posDto = new PositionDTO();
-        posDto.setX(Integer.valueOf(pgBox.x).doubleValue());
-        posDto.setY(Integer.valueOf(pgBox.y).doubleValue());
+        posDto.setX(Integer.valueOf(pgBox.getX()).doubleValue());
+        posDto.setY(Integer.valueOf(pgBox.getY()).doubleValue());
 
         final ProcessGroupDTO pgDto = new ProcessGroupDTO();
         pgDto.setVersionControlInformation(versionControlInfo);

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGList.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGList.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGList.java
new file mode 100644
index 0000000..cf143ef
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGList.java
@@ -0,0 +1,84 @@
+/*
+ * 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.nifi.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ResultWriter;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO;
+import org.apache.nifi.web.api.entity.ProcessGroupEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Command to list process-groups for a given parent process group.
+ */
+public class PGList extends AbstractNiFiCommand {
+
+    public PGList() {
+        super("pg-list");
+    }
+
+    @Override
+    public String getDescription() {
+        return "Returns the process groups contained in the specified process group. If no process group is specified, " +
+                "then the root group will be used.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        addOption(CommandOption.PG_ID.createOption());
+    }
+
+    @Override
+    protected void doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        final FlowClient flowClient = client.getFlowClient();
+
+        // get the optional id of the parent PG, otherwise fallback to the root group
+        String parentPgId = getArg(properties, CommandOption.PG_ID);
+        if (StringUtils.isBlank(parentPgId)) {
+            parentPgId = flowClient.getRootGroupId();
+        }
+
+        final ProcessGroupFlowEntity processGroupFlowEntity = flowClient.getProcessGroup(parentPgId);
+        final ProcessGroupFlowDTO processGroupFlowDTO = processGroupFlowEntity.getProcessGroupFlow();
+        final FlowDTO flowDTO = processGroupFlowDTO.getFlow();
+
+        final List<ProcessGroupEntity> processGroups = new ArrayList<>();
+        if (flowDTO.getProcessGroups() != null) {
+            flowDTO.getProcessGroups().stream().forEach(pg -> processGroups.add(pg));
+        }
+
+        final ResultWriter resultWriter = getResultWriter(properties);
+        resultWriter.writeProcessGroups(processGroups, getContext().getOutput());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetVar.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetVar.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetVar.java
new file mode 100644
index 0000000..afbde9d
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetVar.java
@@ -0,0 +1,116 @@
+/*
+ * 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.nifi.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.web.api.dto.VariableDTO;
+import org.apache.nifi.web.api.dto.VariableRegistryDTO;
+import org.apache.nifi.web.api.entity.VariableEntity;
+import org.apache.nifi.web.api.entity.VariableRegistryEntity;
+import org.apache.nifi.web.api.entity.VariableRegistryUpdateRequestEntity;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Properties;
+
+/**
+ * Command to set the value of a variable in a process group.
+ */
+public class PGSetVar extends AbstractNiFiCommand {
+
+    public PGSetVar() {
+        super("pg-set-var");
+    }
+
+    @Override
+    public String getDescription() {
+        return "Sets the value of a variable in the variable registry for the specified process group.";
+    }
+
+    @Override
+    protected void doInitialize(final Context context) {
+        addOption(CommandOption.PG_ID.createOption());
+        addOption(CommandOption.PG_VAR_NAME.createOption());
+        addOption(CommandOption.PG_VAR_VALUE.createOption());
+    }
+
+    @Override
+    protected void doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
+        final String varName = getRequiredArg(properties, CommandOption.PG_VAR_NAME);
+        final String varVal = getRequiredArg(properties, CommandOption.PG_VAR_VALUE);
+
+        final ProcessGroupClient pgClient = client.getProcessGroupClient();
+        final VariableRegistryEntity variableRegistry = pgClient.getVariables(pgId);
+        final VariableRegistryDTO variableRegistryDTO = variableRegistry.getVariableRegistry();
+
+        final VariableDTO variableDTO = new VariableDTO();
+        variableDTO.setName(varName);
+        variableDTO.setValue(varVal);
+
+        final VariableEntity variableEntity = new VariableEntity();
+        variableEntity.setVariable(variableDTO);
+
+        // take the existing DTO and set only the requested variable for this command
+        variableRegistryDTO.setVariables(Collections.singleton(variableEntity));
+
+        // initiate the update request by posting the updated variable registry
+        final VariableRegistryUpdateRequestEntity createdUpdateRequest = pgClient.updateVariableRegistry(pgId, variableRegistry);
+
+        // poll the update request for up to 30 seconds to see if it has completed
+        // if it doesn't complete then an exception will be thrown, but in either case the request will be deleted
+        final String updateRequestId = createdUpdateRequest.getRequest().getRequestId();
+        try {
+            boolean completed = false;
+            for (int i = 0; i < 30; i++) {
+                final VariableRegistryUpdateRequestEntity updateRequest =
+                        pgClient.getVariableRegistryUpdateRequest(pgId, updateRequestId);
+
+                if (updateRequest != null && updateRequest.getRequest().isComplete()) {
+                    completed = true;
+                    break;
+                } else {
+                    try {
+                        if (getContext().isInteractive()) {
+                            println("Waiting for update request to complete...");
+                        }
+                        Thread.sleep(2000);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            if (!completed) {
+                throw new NiFiClientException("Unable to update variables of process group, cancelling request");
+            }
+
+        } finally {
+            pgClient.deleteVariableRegistryUpdateRequest(pgId, updateRequestId);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStart.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStart.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStart.java
index c2d3752..938d603 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStart.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStart.java
@@ -39,6 +39,11 @@ public class PGStart extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Starts the given process group which starts any enabled and valid components contained in that group.";
+    }
+
+    @Override
     protected void doInitialize(final Context context) {
         addOption(CommandOption.PG_ID.createOption());
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStop.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStop.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStop.java
index 036b7db..cd34a24 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStop.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGStop.java
@@ -39,6 +39,11 @@ public class PGStop extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Stops the given process group which stops any running components in the given group.";
+    }
+
+    @Override
     protected void doInitialize(final Context context) {
         addOption(CommandOption.PG_ID.createOption());
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1c80800/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java
index d125ce5..ae520d6 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java
@@ -38,6 +38,11 @@ public class CreateRegistryClient extends AbstractNiFiCommand {
     }
 
     @Override
+    public String getDescription() {
+        return "Creates a registry client using the provided information.";
+    }
+
+    @Override
     public void doInitialize(final Context context) {
         addOption(CommandOption.REGISTRY_CLIENT_NAME.createOption());
         addOption(CommandOption.REGISTRY_CLIENT_URL.createOption());