You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by mi...@apache.org on 2016/11/01 17:12:06 UTC

[4/7] incubator-rya git commit: RYA-110 Add Rya Uninstall command to the Rya Console.

RYA-110 Add Rya Uninstall command to the Rya Console.

Closes #94


Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/5c54c293
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/5c54c293
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/5c54c293

Branch: refs/heads/master
Commit: 5c54c2935a5de4a5bec133708ace2792084ecea3
Parents: ab4fca4
Author: Kevin Chilton <ke...@parsons.com>
Authored: Thu Sep 15 16:12:00 2016 -0400
Committer: Aaron Mihalik <aa...@gmail.com>
Committed: Tue Nov 1 10:54:04 2016 -0400

----------------------------------------------------------------------
 .../org/apache/rya/api/client/RyaClient.java    | 14 ++-
 .../org/apache/rya/api/client/Uninstall.java    | 38 ++++++++
 .../accumulo/AccumuloConnectionDetails.java     |  2 +-
 .../accumulo/AccumuloRyaClientFactory.java      |  3 +-
 .../api/client/accumulo/AccumuloUninstall.java  | 96 ++++++++++++++++++++
 .../api/client/accumulo/AccumuloInstallIT.java  |  7 +-
 .../client/accumulo/AccumuloUninstallIT.java    | 82 +++++++++++++++++
 extras/rya.console/pom.xml                      |  9 +-
 .../org/apache/rya/shell/RyaAdminCommands.java  | 35 ++++++-
 .../apache/rya/shell/util/UninstallPrompt.java  | 58 ++++++++++++
 .../apache/rya/shell/RyaAdminCommandsTest.java  | 67 ++++++++++++--
 .../src/test/resources/RyaShellTest-context.xml |  5 +
 12 files changed, 395 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/common/rya.api/src/main/java/org/apache/rya/api/client/RyaClient.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/org/apache/rya/api/client/RyaClient.java b/common/rya.api/src/main/java/org/apache/rya/api/client/RyaClient.java
index b290957..d4b5047 100644
--- a/common/rya.api/src/main/java/org/apache/rya/api/client/RyaClient.java
+++ b/common/rya.api/src/main/java/org/apache/rya/api/client/RyaClient.java
@@ -40,6 +40,7 @@ public class RyaClient {
     private final ListInstances listInstances;
     private final AddUser addUser;
     private final RemoveUser removeUser;
+    private final Uninstall uninstall;
 
     /**
      * Constructs an instance of {@link RyaClient}.
@@ -53,16 +54,18 @@ public class RyaClient {
             final InstanceExists instanceExists,
             final ListInstances listInstances,
             final AddUser addUser,
-            final RemoveUser removeUser) {
+            final RemoveUser removeUser,
+            final Uninstall uninstall) {
         this.install = requireNonNull(install);
         this.createPcj = requireNonNull(createPcj);
         this.deletePcj = requireNonNull(deletePcj);
-        bactchUpdatePCJ = requireNonNull(batchUpdatePcj);
+        this.bactchUpdatePCJ = requireNonNull(batchUpdatePcj);
         this.getInstanceDetails = requireNonNull(getInstanceDetails);
         this.instanceExists = requireNonNull(instanceExists);
         this.listInstances = requireNonNull(listInstances);
         this.addUser = requireNonNull(addUser);
         this.removeUser = requireNonNull(removeUser);
+        this.uninstall = requireNonNull(uninstall);
     }
 
     /**
@@ -130,4 +133,11 @@ public class RyaClient {
     public RemoveUser getRemoveUser() {
         return removeUser;
     }
+
+    /**
+     * @return An instance of {@link Uninstall} that is connected to a Rya storage.
+     */
+    public Uninstall getUninstall() {
+        return uninstall;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/common/rya.api/src/main/java/org/apache/rya/api/client/Uninstall.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/org/apache/rya/api/client/Uninstall.java b/common/rya.api/src/main/java/org/apache/rya/api/client/Uninstall.java
new file mode 100644
index 0000000..b88f305
--- /dev/null
+++ b/common/rya.api/src/main/java/org/apache/rya/api/client/Uninstall.java
@@ -0,0 +1,38 @@
+/**
+ * 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.rya.api.client;
+
+import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * Uninstalls an instannce of Rya.
+ */
+@DefaultAnnotation(NonNull.class)
+public interface Uninstall {
+
+    /**
+     * Uninstalls an instance of Rya.
+     *
+     * @param instanceName - Indicates which Rya instance to uninstalled. (not null)
+     * @throws InstanceDoesNotExistException The instance of Rya already doesn't exist.
+     * @throws RyaClientException Something caused the command to fail.
+     */
+    public void uninstall(final String instanceName) throws InstanceDoesNotExistException, RyaClientException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloConnectionDetails.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloConnectionDetails.java b/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloConnectionDetails.java
index a746bf1..f3753a8 100644
--- a/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloConnectionDetails.java
+++ b/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloConnectionDetails.java
@@ -33,7 +33,7 @@ public class AccumuloConnectionDetails {
     private final String username;
     private final char[] password;
     private final String instanceName;
-    private final String zookeepers;
+    private final String zookeepers; 
 
     /**
      * Constructs an instance of {@link AccumuloConnectionDetails}.

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloRyaClientFactory.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloRyaClientFactory.java b/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloRyaClientFactory.java
index 02c461f..0e642d9 100644
--- a/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloRyaClientFactory.java
+++ b/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloRyaClientFactory.java
@@ -57,6 +57,7 @@ public class AccumuloRyaClientFactory {
                 new AccumuloInstanceExists(connectionDetails, connector),
                 new AccumuloListInstances(connectionDetails, connector),
                 new AccumuloAddUser(connectionDetails, connector),
-                new AccumuloRemoveUser(connectionDetails, connector));
+                new AccumuloRemoveUser(connectionDetails, connector),
+                new AccumuloUninstall(connectionDetails, connector));
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloUninstall.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloUninstall.java b/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloUninstall.java
new file mode 100644
index 0000000..96de16c
--- /dev/null
+++ b/extras/indexing/src/main/java/org/apache/rya/api/client/accumulo/AccumuloUninstall.java
@@ -0,0 +1,96 @@
+/**
+ * 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.rya.api.client.accumulo;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.admin.TableOperations;
+import org.apache.log4j.Logger;
+import org.apache.rya.accumulo.utils.RyaTableNames;
+import org.apache.rya.api.client.InstanceDoesNotExistException;
+import org.apache.rya.api.client.InstanceExists;
+import org.apache.rya.api.client.RyaClientException;
+import org.apache.rya.api.client.Uninstall;
+import org.apache.rya.api.instance.RyaDetailsRepository.RyaDetailsRepositoryException;
+import org.apache.rya.indexing.pcj.storage.PrecomputedJoinStorage.PCJStorageException;
+
+import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * An Accumulo implementation of the {@link Uninstall} command.
+ */
+@DefaultAnnotation(NonNull.class)
+public class AccumuloUninstall extends AccumuloCommand implements Uninstall {
+
+    private static final Logger log = Logger.getLogger(AccumuloUninstall.class);
+
+    private final InstanceExists instanceExists;
+
+    /**
+     * Constructs an instance of {@link AccumuloUninstall}.
+     *
+     * @param connectionDetails - Details about the values that were used to create
+     *   the connector to the cluster. (not null)
+     * @param connector - Provides programmatic access to the instance of Accumulo
+     *   that hosts Rya instance. (not null)
+     */
+    public AccumuloUninstall(final AccumuloConnectionDetails connectionDetails, final Connector connector) {
+        super(connectionDetails, connector);
+        instanceExists = new AccumuloInstanceExists(connectionDetails, connector);
+    }
+
+    @Override
+    public void uninstall(final String ryaInstanceName) throws InstanceDoesNotExistException, RyaClientException {
+        requireNonNull(ryaInstanceName);
+
+        // Ensure the Rya Instance exists.
+        if(!instanceExists.exists(ryaInstanceName)) {
+            throw new InstanceDoesNotExistException(String.format("There is no Rya instance named '%s'.", ryaInstanceName));
+        }
+
+        try {
+            // Build the list of tables that are present within the Rya instance.
+            final List<String> tables = new RyaTableNames().getTableNames(ryaInstanceName, getConnector());
+
+            // Delete them.
+            final TableOperations tableOps = getConnector().tableOperations();
+            for(final String table : tables) {
+                try {
+                    tableOps.delete(table);
+                } catch(final TableNotFoundException e) {
+                    log.warn("Uninstall could not delete table named '" + table + "' because it does not exist. " +
+                            "Something else is also deleting tables.");
+                }
+            }
+        } catch (PCJStorageException | RyaDetailsRepositoryException e) {
+            throw new RyaClientException("Could not uninstall the Rya instance named '" + ryaInstanceName +
+                    "' because we could not determine which tables are associated with it.", e);
+        } catch (AccumuloException | AccumuloSecurityException e) {
+            throw new RyaClientException("Could not uninstall the Rya instance named '" + ryaInstanceName +
+                    "' because of a problem interacting with Accumulo..", e);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloInstallIT.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloInstallIT.java b/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloInstallIT.java
index a8e4e5c..739d216 100644
--- a/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloInstallIT.java
+++ b/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloInstallIT.java
@@ -18,10 +18,10 @@
  */
 package org.apache.rya.api.client.accumulo;
 
+import static org.junit.Assert.assertTrue;
+
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
-import org.junit.Test;
-
 import org.apache.rya.accumulo.AccumuloITBase;
 import org.apache.rya.api.client.Install;
 import org.apache.rya.api.client.Install.DuplicateInstanceNameException;
@@ -30,6 +30,7 @@ import org.apache.rya.api.client.InstanceExists;
 import org.apache.rya.api.client.RyaClientException;
 import org.apache.rya.api.instance.RyaDetailsRepository.NotInitializedException;
 import org.apache.rya.api.instance.RyaDetailsRepository.RyaDetailsRepositoryException;
+import org.junit.Test;
 
 /**
  * Integration tests the methods of {@link AccumuloInstall}.
@@ -61,7 +62,7 @@ public class AccumuloInstallIT extends AccumuloITBase {
 
         // Check that the instance exists.
         final InstanceExists instanceExists = new AccumuloInstanceExists(connectionDetails, getConnector());
-        instanceExists.exists(instanceName);
+        assertTrue( instanceExists.exists(instanceName) );
     }
 
     @Test(expected = DuplicateInstanceNameException.class)

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloUninstallIT.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloUninstallIT.java b/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloUninstallIT.java
new file mode 100644
index 0000000..7f79efb
--- /dev/null
+++ b/extras/indexing/src/test/java/org/apache/rya/api/client/accumulo/AccumuloUninstallIT.java
@@ -0,0 +1,82 @@
+/**
+ * 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.rya.api.client.accumulo;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.rya.accumulo.AccumuloITBase;
+import org.apache.rya.api.client.Install;
+import org.apache.rya.api.client.Install.InstallConfiguration;
+import org.apache.rya.api.client.InstanceDoesNotExistException;
+import org.apache.rya.api.client.InstanceExists;
+import org.apache.rya.api.client.Uninstall;
+import org.junit.Test;
+
+/**
+ * Integration tests the methods of {@link AccumuloUninstall}.
+ */
+public class AccumuloUninstallIT extends AccumuloITBase {
+
+    @Test(expected = InstanceDoesNotExistException.class)
+    public void instanceDoesNotExist() throws Exception {
+        final AccumuloConnectionDetails connectionDetails = new AccumuloConnectionDetails(
+                getUsername(),
+                getPassword().toCharArray(),
+                getInstanceName(),
+                getZookeepers());
+
+        new AccumuloUninstall(connectionDetails, getConnector()).uninstall("instance_that_does_not_exist");
+    }
+
+    @Test
+    public void uninstall() throws Exception {
+        // Install an instance of Rya.
+        final InstallConfiguration installConfig = InstallConfiguration.builder()
+                .setEnableTableHashPrefix(true)
+                .setEnableEntityCentricIndex(true)
+                .setEnableFreeTextIndex(true)
+                .setEnableTemporalIndex(true)
+                .setEnablePcjIndex(true)
+                .setEnableGeoIndex(true)
+                .setFluoPcjAppName("fluo_app_name")
+                .build();
+
+        final AccumuloConnectionDetails connectionDetails = new AccumuloConnectionDetails(
+                getUsername(),
+                getPassword().toCharArray(),
+                getInstanceName(),
+                getZookeepers());
+
+        final Install install = new AccumuloInstall(connectionDetails, getConnector());
+        final String ryaInstanceName = "testInstance_";
+        install.install(ryaInstanceName, installConfig);
+
+        // Check that the instance exists.
+        final InstanceExists instanceExists = new AccumuloInstanceExists(connectionDetails, getConnector());
+        assertTrue( instanceExists.exists(ryaInstanceName) );
+
+        // Uninstall the instance of Rya.
+        final Uninstall uninstall = new AccumuloUninstall(connectionDetails, getConnector());
+        uninstall.uninstall(ryaInstanceName);
+
+        // Verify that it no longer exists.
+        assertFalse( instanceExists.exists(ryaInstanceName) );
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/rya.console/pom.xml
----------------------------------------------------------------------
diff --git a/extras/rya.console/pom.xml b/extras/rya.console/pom.xml
index 09a470f..779cf42 100644
--- a/extras/rya.console/pom.xml
+++ b/extras/rya.console/pom.xml
@@ -155,13 +155,12 @@
                                     <mainClass>org.springframework.shell.Bootstrap</mainClass>
                                 </transformer>
                             </transformers>
-
-                            <!--
-                                Shading signed JARs will fail without this.
-                                http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
-                            -->
                             <filters>
                                 <filter>
+                                    <!--
+                                        Shading signed JARs will fail without this.
+                                        http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
+                                    -->
                                     <artifact>*:*</artifact>
                                     <excludes>
                                         <exclude>META-INF/*.SF</exclude>

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/rya.console/src/main/java/org/apache/rya/shell/RyaAdminCommands.java
----------------------------------------------------------------------
diff --git a/extras/rya.console/src/main/java/org/apache/rya/shell/RyaAdminCommands.java b/extras/rya.console/src/main/java/org/apache/rya/shell/RyaAdminCommands.java
index 7e8b71f..5493170 100644
--- a/extras/rya.console/src/main/java/org/apache/rya/shell/RyaAdminCommands.java
+++ b/extras/rya.console/src/main/java/org/apache/rya/shell/RyaAdminCommands.java
@@ -37,6 +37,7 @@ import org.apache.rya.shell.util.InstallPrompt;
 import org.apache.rya.shell.util.InstanceNamesFormatter;
 import org.apache.rya.shell.util.RyaDetailsFormatter;
 import org.apache.rya.shell.util.SparqlPrompt;
+import org.apache.rya.shell.util.UninstallPrompt;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.shell.core.CommandMarker;
 import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
@@ -64,6 +65,7 @@ public class RyaAdminCommands implements CommandMarker {
     private final SharedShellState state;
     private final InstallPrompt installPrompt;
     private final SparqlPrompt sparqlPrompt;
+    private final UninstallPrompt uninstallPrompt;
 
     /**
      * Constructs an instance of {@link RyaAdminCommands}.
@@ -71,12 +73,18 @@ public class RyaAdminCommands implements CommandMarker {
      * @param state - Holds shared state between all of the command classes. (not null)
      * @param installPrompt - Prompts a user for installation details. (not null)
      * @param sparqlPrompt - Prompts a user for a SPARQL query. (not null)
+     * @param uninstallPrompt - Prompts a user when uninstalling. (not null)
      */
     @Autowired
-    public RyaAdminCommands(final SharedShellState state, final InstallPrompt installPrompt, final SparqlPrompt sparqlPrompt) {
+    public RyaAdminCommands(
+            final SharedShellState state,
+            final InstallPrompt installPrompt,
+            final SparqlPrompt sparqlPrompt,
+            final UninstallPrompt uninstallPrompt) {
         this.state = requireNonNull( state );
         this.installPrompt = requireNonNull(installPrompt);
         this.sparqlPrompt = requireNonNull(sparqlPrompt);
+        this.uninstallPrompt = requireNonNull(uninstallPrompt);
     }
 
     /**
@@ -290,4 +298,29 @@ public class RyaAdminCommands implements CommandMarker {
             throw new RuntimeException("The user's access could not be revoked. Provided reason: " + e.getMessage(), e);
         }
     }
+
+    @CliCommand(value = UNINSTALL_CMD, help = "Uninstall an instance of Rya.")
+    public String uninstall() {
+        // Fetch the command that is connected to the store.
+        final ShellState shellState = state.getShellState();
+        final RyaClient commands = shellState.getConnectedCommands().get();
+        final String ryaInstanceName = shellState.getRyaInstanceName().get();
+
+        try {
+            // Make sure the user meant to uninstall the Rya instance.
+            if(!uninstallPrompt.promptAreYouSure(ryaInstanceName)) {
+                return "Cancelled.";
+            }
+
+            // Perform the uninstall.
+            commands.getUninstall().uninstall(ryaInstanceName);
+
+        } catch (final InstanceDoesNotExistException e) {
+            throw new RuntimeException(String.format("A Rya instance named '%s' does not exist.", ryaInstanceName), e);
+        } catch (final IOException | RyaClientException e) {
+            throw new RuntimeException("The Rya instance could not be uninstalled. Provided reason: " + e.getMessage(), e);
+        }
+
+        return "The Rya instance named '" + ryaInstanceName +"' has been uninstalled.";
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/rya.console/src/main/java/org/apache/rya/shell/util/UninstallPrompt.java
----------------------------------------------------------------------
diff --git a/extras/rya.console/src/main/java/org/apache/rya/shell/util/UninstallPrompt.java b/extras/rya.console/src/main/java/org/apache/rya/shell/util/UninstallPrompt.java
new file mode 100644
index 0000000..628727c
--- /dev/null
+++ b/extras/rya.console/src/main/java/org/apache/rya/shell/util/UninstallPrompt.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rya.shell.util;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+
+import com.google.common.base.Optional;
+
+import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import jline.console.ConsoleReader;
+
+/**
+ * A mechanism for prompting a user of the application to ensure they want to
+ * uninstall an instance of Rya.
+ */
+@DefaultAnnotation(NonNull.class)
+public interface UninstallPrompt {
+
+    /**
+     * Prompt the user to make sure they want to uninstall the instance of Rya.
+     *
+     * @param ryaInstanceName - The name of the Rya instance being prompted for. (not null)
+     * @return The value they entered.
+     * @throws IOException There was a problem reading the values.
+     */
+    public boolean promptAreYouSure(final String ryaInstanceName) throws IOException;
+
+    /**
+     * Prompts a user for uninstall information using a JLine {@link ConsoleReader}.
+     */
+    public static class JLineUninstallPrompt extends JLinePrompt implements UninstallPrompt {
+        @Override
+        public boolean promptAreYouSure(final String ryaInstanceName) throws IOException {
+            requireNonNull(ryaInstanceName);
+            return promptBoolean("Are you sure you want to uninstall this instance of Rya named '" +
+                    ryaInstanceName + "'? ", Optional.<Boolean>absent());
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/rya.console/src/test/java/org/apache/rya/shell/RyaAdminCommandsTest.java
----------------------------------------------------------------------
diff --git a/extras/rya.console/src/test/java/org/apache/rya/shell/RyaAdminCommandsTest.java b/extras/rya.console/src/test/java/org/apache/rya/shell/RyaAdminCommandsTest.java
index 4aa1210..a5b73f2 100644
--- a/extras/rya.console/src/test/java/org/apache/rya/shell/RyaAdminCommandsTest.java
+++ b/extras/rya.console/src/test/java/org/apache/rya/shell/RyaAdminCommandsTest.java
@@ -21,6 +21,7 @@ package org.apache.rya.shell;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -41,6 +42,7 @@ import org.apache.rya.api.client.ListInstances;
 import org.apache.rya.api.client.RemoveUser;
 import org.apache.rya.api.client.RyaClient;
 import org.apache.rya.api.client.RyaClientException;
+import org.apache.rya.api.client.Uninstall;
 import org.apache.rya.api.client.accumulo.AccumuloConnectionDetails;
 import org.apache.rya.api.instance.RyaDetails;
 import org.apache.rya.api.instance.RyaDetails.EntityCentricIndexDetails;
@@ -54,6 +56,7 @@ import org.apache.rya.api.instance.RyaDetails.ProspectorDetails;
 import org.apache.rya.api.instance.RyaDetails.TemporalIndexDetails;
 import org.apache.rya.shell.util.InstallPrompt;
 import org.apache.rya.shell.util.SparqlPrompt;
+import org.apache.rya.shell.util.UninstallPrompt;
 import org.junit.Test;
 
 import com.google.common.base.Optional;
@@ -84,7 +87,7 @@ public class RyaAdminCommandsTest {
         when(mockSparqlPrompt.getSparql()).thenReturn(sparql);
 
         // Execute the command.
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mockSparqlPrompt);
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mockSparqlPrompt, mock(UninstallPrompt.class));
         final String message = commands.createPcj();
 
         // Verify the values that were provided to the command were passed through to CreatePCJ.
@@ -111,7 +114,7 @@ public class RyaAdminCommandsTest {
         // Execute the command.
         final String pcjId = "123412342";
 
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class));
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), mock(UninstallPrompt.class));
         final String message = commands.deletePcj(pcjId);
 
         // Verify the values that were provided to the command were passed through to the DeletePCJ.
@@ -166,7 +169,7 @@ public class RyaAdminCommandsTest {
         state.connectedToInstance(instanceName);
 
         // Execute the command.
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class));
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), mock(UninstallPrompt.class));
         final String message = commands.getInstanceDetails();
 
         // Verify the values that were provided to the command were passed through to the GetInstanceDetails.
@@ -228,7 +231,7 @@ public class RyaAdminCommandsTest {
         when(mockInstallPrompt.promptInstallConfiguration()).thenReturn( installConfig );
         when(mockInstallPrompt.promptVerified(eq(instanceName), eq(installConfig))).thenReturn(true);
 
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mockInstallPrompt, mock(SparqlPrompt.class));
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mockInstallPrompt, mock(SparqlPrompt.class), mock(UninstallPrompt.class));
         final String message = commands.install();
 
         // Verify the values that were provided to the command were passed through to the Install.
@@ -254,7 +257,7 @@ public class RyaAdminCommandsTest {
         state.connectedToInstance("b");
 
         // Execute the command.
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class));
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), mock(UninstallPrompt.class));
         final String message = commands.listInstances();
 
         // Verify a message is returned that lists the the instances.
@@ -280,7 +283,7 @@ public class RyaAdminCommandsTest {
         state.connectedToInstance("test_instance");
 
         // Execute the command.
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class));
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), mock(UninstallPrompt.class));
         commands.addUser("alice");
 
         // Verify the add request was forwarded to the client.
@@ -300,10 +303,58 @@ public class RyaAdminCommandsTest {
         state.connectedToInstance("test_instance");
 
         // Execute the command.
-        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class));
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), mock(UninstallPrompt.class));
         commands.removeUser("alice");
 
         // Verify the add request was forwarded to the client.
         verify(mockRemoveUser).removeUser(eq("test_instance"), eq("alice"));
     }
-}
\ No newline at end of file
+
+    @Test
+    public void uninstall_yes() throws Exception {
+        // Mock the object that performs the Uninstall command.
+        final Uninstall mockUninstall = mock(Uninstall.class);
+
+        // Mock a prompt that says the user does want to uninstall it.
+        final UninstallPrompt uninstallPrompt = mock(UninstallPrompt.class);
+        when(uninstallPrompt.promptAreYouSure( eq("test_instance") )).thenReturn(true);
+
+        final RyaClient mockClient = mock(RyaClient.class);
+        when(mockClient.getUninstall()).thenReturn( mockUninstall );
+
+        final SharedShellState state = new SharedShellState();
+        state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockClient);
+        state.connectedToInstance("test_instance");
+
+        // Execute the command.
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), uninstallPrompt);
+        commands.uninstall();
+
+        // Verify the request was forwarded to the client.
+        verify(mockUninstall).uninstall(eq("test_instance"));
+    }
+
+    @Test
+    public void uninstall_no() throws Exception {
+        // Mock the object that performs the Uninstall command.
+        final Uninstall mockUninstall = mock(Uninstall.class);
+
+        // Mock a prompt that says the user does want to uninstall it.
+        final UninstallPrompt uninstallPrompt = mock(UninstallPrompt.class);
+        when(uninstallPrompt.promptAreYouSure( eq("test_instance") )).thenReturn(false);
+
+        final RyaClient mockClient = mock(RyaClient.class);
+        when(mockClient.getUninstall()).thenReturn( mockUninstall );
+
+        final SharedShellState state = new SharedShellState();
+        state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockClient);
+        state.connectedToInstance("test_instance");
+
+        // Execute the command.
+        final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class), uninstallPrompt);
+        commands.uninstall();
+
+        // Verify the request was forwarded to the client.
+        verify(mockUninstall, never()).uninstall(eq("test_instance"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/5c54c293/extras/rya.console/src/test/resources/RyaShellTest-context.xml
----------------------------------------------------------------------
diff --git a/extras/rya.console/src/test/resources/RyaShellTest-context.xml b/extras/rya.console/src/test/resources/RyaShellTest-context.xml
index bb91f68..f841129 100644
--- a/extras/rya.console/src/test/resources/RyaShellTest-context.xml
+++ b/extras/rya.console/src/test/resources/RyaShellTest-context.xml
@@ -45,6 +45,11 @@
     <bean id="sparqlPrompt" class="org.mockito.Mockito" factory-method="mock">
         <constructor-arg value="org.apache.rya.shell.util.SparqlPrompt"/>
     </bean>
+    
+    <!-- We use a mock Uninstall Prompt here to simulate a user entering the installation configuration. -->
+    <bean id="uninstallPrompt" class="org.mockito.Mockito" factory-method="mock">
+        <constructor-arg value="org.apache.rya.shell.util.UninstallPrompt"/>
+    </bean>
 
     <!-- Define each of the beans that hold onto commands used by the shell. -->
     <bean id="ryaConnectionCommands" class="org.apache.rya.shell.RyaConnectionCommands" />