You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by th...@apache.org on 2015/01/16 07:39:24 UTC

svn commit: r1652348 - in /lucene/dev/branches/lucene_solr_5_0: ./ solr/ solr/CHANGES.txt solr/bin/ solr/bin/solr solr/bin/solr.cmd solr/core/ solr/core/src/java/org/apache/solr/util/SolrCLI.java

Author: thelabdude
Date: Fri Jan 16 06:39:24 2015
New Revision: 1652348

URL: http://svn.apache.org/r1652348
Log:
SOLR-6981: add a delete action to the bin/solr script

Modified:
    lucene/dev/branches/lucene_solr_5_0/   (props changed)
    lucene/dev/branches/lucene_solr_5_0/solr/   (props changed)
    lucene/dev/branches/lucene_solr_5_0/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/lucene_solr_5_0/solr/bin/   (props changed)
    lucene/dev/branches/lucene_solr_5_0/solr/bin/solr
    lucene/dev/branches/lucene_solr_5_0/solr/bin/solr.cmd
    lucene/dev/branches/lucene_solr_5_0/solr/core/   (props changed)
    lucene/dev/branches/lucene_solr_5_0/solr/core/src/java/org/apache/solr/util/SolrCLI.java

Modified: lucene/dev/branches/lucene_solr_5_0/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_5_0/solr/CHANGES.txt?rev=1652348&r1=1652347&r2=1652348&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_5_0/solr/CHANGES.txt (original)
+++ lucene/dev/branches/lucene_solr_5_0/solr/CHANGES.txt Fri Jan 16 06:39:24 2015
@@ -663,6 +663,9 @@ Other Changes
 * SOLR-6982: bin/solr and SolrCLI should support SSL-related Java System Properties
   (Timothy Potter)
 
+* SOLR-6981: Add a delete action to the bin/solr script to allow deleting of cores /
+  collections (with delete collection config directory from ZK) (Timothy Potter)
+
 ==================  4.10.3 ==================
 
 Bug Fixes

Modified: lucene/dev/branches/lucene_solr_5_0/solr/bin/solr
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_5_0/solr/bin/solr?rev=1652348&r1=1652347&r2=1652348&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_5_0/solr/bin/solr (original)
+++ lucene/dev/branches/lucene_solr_5_0/solr/bin/solr Fri Jan 16 06:39:24 2015
@@ -129,7 +129,7 @@ function print_usage() {
   if [ -z "$CMD" ]; then
     echo ""
     echo "Usage: solr COMMAND OPTIONS"
-    echo "       where COMMAND is one of: start, stop, restart, status, healthcheck, create, create_core, create_collection"
+    echo "       where COMMAND is one of: start, stop, restart, status, healthcheck, create, create_core, create_collection, delete"
     echo ""
     echo "  Standalone server example (start Solr running in the background on port 8984):"
     echo ""
@@ -229,6 +229,23 @@ function print_usage() {
     echo ""
     echo "    bin/solr create_collection -help"
     echo ""
+  elif [ "$CMD" == "delete" ]; then
+    echo ""
+    echo "Usage: solr delete [-c name] [-deleteConfig true|false] [-p port]"
+    echo ""
+    echo "  Deletes a core or collection depending on whether Solr is running in standalone (core) or SolrCloud"
+    echo "  mode (collection). If you're deleting a collection in SolrCloud mode, the default behavior is to also"
+    echo "  delete the configuration directory from ZooKeeper so long as it is not being used by another collection."
+    echo "  You can override this behavior by passing -deleteConfig false when running this command."
+    echo ""
+    echo "  -c <name>               Name of the core / collection to delete"
+    echo ""
+    echo "  -deleteConfig <boolean> Delete the configuration directory from ZooKeeper; default is true"
+    echo ""
+    echo "  -p <port>               Port of a local Solr instance where you want to delete the core/collection"
+    echo "                            If not specified, the script will search the local system for a running"
+    echo "                            Solr instance and will use the port of the first server it finds."
+    echo ""
   elif [ "$CMD" == "create_core" ]; then
     echo ""
     echo "Usage: solr create_core [-c core] [-d confdir] [-p port]"
@@ -645,6 +662,87 @@ if [[ "$SCRIPT_CMD" == "create" || "$SCR
   fi
 fi
 
+# delete a core or collection
+if [[ "$SCRIPT_CMD" == "delete" ]]; then
+
+  if [ $# -gt 0 ]; then
+    while true; do
+      case $1 in
+          -c|-core|-collection)
+              if [[ -z "$2" || "${2:0:1}" == "-" ]]; then
+                print_usage "$SCRIPT_CMD" "name is required when using the $1 option!"
+                exit 1
+              fi
+              DELETE_NAME=$2
+              shift 2
+          ;;
+          -p|-port)
+              if [[ -z "$2" || "${2:0:1}" == "-" ]]; then
+                print_usage "$SCRIPT_CMD" "Solr port is required when using the $1 option!"
+                exit 1
+              fi
+              DELETE_PORT="$2"
+              shift 2
+          ;;
+          -deleteConfig)
+              if [[ -z "$2" || "${2:0:1}" == "-" ]]; then
+                print_usage "$SCRIPT_CMD" "true|false is required when using the $1 option!"
+                exit 1
+              fi
+              DELETE_CONFIG="$2"
+              shift 2
+          ;;
+          -help|-usage)
+              print_usage "$SCRIPT_CMD"
+              exit 0
+          ;;
+          --)
+              shift
+              break
+          ;;
+          *)
+              if [ "$1" != "" ]; then
+                print_usage "$SCRIPT_CMD" "Unrecognized or misplaced argument: $1!"
+                exit 1
+              else
+                break # out-of-args, stop looping
+              fi
+          ;;
+      esac
+    done
+  fi
+
+  if [ -z "$DELETE_NAME" ]; then
+    echo "Name (-c) argument is required!"
+    print_usage "$SCRIPT_CMD"
+    exit 1
+  fi
+
+  # If not defined, use the collection name for the name of the configuration in ZooKeeper
+  if [ -z "$DELETE_CONFIG" ]; then
+    DELETE_CONFIG=true
+  fi
+
+  if [ -z "$DELETE_PORT" ]; then
+    for ID in `ps auxww | grep java | grep start.jar | awk '{print $2}' | sort -r`
+      do
+        port=`jetty_port "$ID"`
+        if [ "$port" != "" ]; then
+          DELETE_PORT=$port
+          break
+        fi
+    done
+  fi
+
+  if [ -z "$DELETE_PORT" ]; then
+    echo "Failed to determine the port of a local Solr instance, cannot delete $DELETE_NAME!"
+    exit 1
+  fi
+
+  run_tool delete -name $DELETE_NAME -deleteConfig $DELETE_CONFIG \
+    -solrUrl $SOLR_URL_SCHEME://localhost:$DELETE_PORT/solr
+  exit $?
+fi
 
 # verify the command given is supported
 if [ "$SCRIPT_CMD" != "stop" ] && [ "$SCRIPT_CMD" != "start" ] && [ "$SCRIPT_CMD" != "restart" ] && [ "$SCRIPT_CMD" != "status" ]; then

Modified: lucene/dev/branches/lucene_solr_5_0/solr/bin/solr.cmd
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_5_0/solr/bin/solr.cmd?rev=1652348&r1=1652347&r2=1652348&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_5_0/solr/bin/solr.cmd (original)
+++ lucene/dev/branches/lucene_solr_5_0/solr/bin/solr.cmd Fri Jan 16 06:39:24 2015
@@ -96,6 +96,11 @@ IF "%1"=="create_collection" (
   SHIFT
   goto parse_create_args
 )
+IF "%1"=="delete" (
+  set SCRIPT_CMD=delete
+  SHIFT
+  goto parse_delete_args
+)
 goto parse_args
 
 :usage
@@ -111,12 +116,13 @@ IF "%SCRIPT_CMD%"=="healthcheck" goto he
 IF "%SCRIPT_CMD%"=="create" goto create_usage
 IF "%SCRIPT_CMD%"=="create_core" goto create_core_usage
 IF "%SCRIPT_CMD%"=="create_collection" goto create_collection_usage
+IF "%SCRIPT_CMD%"=="delete" goto delete_usage
 goto done
 
 :script_usage
 @echo.
 @echo Usage: solr COMMAND OPTIONS
-@echo        where COMMAND is one of: start, stop, restart, healthcheck, create, create_core, create_collection
+@echo        where COMMAND is one of: start, stop, restart, healthcheck, create, create_core, create_collection, delete
 @echo.
 @echo   Standalone server example (start Solr running in the background on port 8984):
 @echo.
@@ -215,6 +221,25 @@ echo     bin\solr create_collection -hel
 echo.
 goto done
 
+:delete_usage
+echo.
+echo Usage: solr delete [-c name] [-deleteConfig boolean] [-p port]
+echo.
+echo  Deletes a core or collection depending on whether Solr is running in standalone (core) or SolrCloud
+echo  mode (collection). If you're deleting a collection in SolrCloud mode, the default behavior is to also
+echo  delete the configuration directory from ZooKeeper so long as it is not being used by another collection.
+echo  You can override this behavior by passing -deleteConfig false when running this command.
+echo.
+echo   -c name     Name of core to create
+echo.
+echo   -deleteConfig boolean Delete the configuration directory from ZooKeeper; default is true
+echo.
+echo   -p port     Port of a local Solr instance where you want to create the new core
+echo                 If not specified, the script will search the local system for a running
+echo                 Solr instance and will use the port of the first server it finds.
+echo.
+goto done
+
 :create_core_usage
 echo.
 echo Usage: solr create_core [-c name] [-d confdir] [-p port]
@@ -1123,6 +1148,72 @@ if "%SCRIPT_CMD%"=="create_core" (
 
 goto done
 
+:parse_delete_args
+IF [%1]==[] goto run_delete
+IF "%1"=="-c" goto set_delete_name
+IF "%1"=="-core" goto set_delete_name
+IF "%1"=="-collection" goto set_delete_name
+IF "%1"=="-p" goto set_delete_port
+IF "%1"=="-port" goto set_delete_port
+IF "%1"=="-deleteConfig" goto set_delete_config
+IF "%1"=="-help" goto usage
+IF "%1"=="-usage" goto usage
+IF "%1"=="/?" goto usage
+goto run_delete
+
+:set_delete_name
+set DELETE_NAME=%~2
+SHIFT
+SHIFT
+goto parse_delete_args
+
+:set_delete_port
+set DELETE_PORT=%~2
+SHIFT
+SHIFT
+goto parse_delete_args
+
+:set_delete_config
+set DELETE_CONFIG=%~2
+SHIFT
+SHIFT
+goto parse_delete_args
+
+:run_delete
+IF "!DELETE_NAME!"=="" (
+  set "SCRIPT_ERROR=Name (-c) is a required parameter for %SCRIPT_CMD%"
+  goto invalid_cmd_line
+)
+
+REM Find a port that Solr is running on
+if "!DELETE_PORT!"=="" (
+  for /f "usebackq" %%i in (`dir /b %SOLR_TIP%\bin ^| findstr /i "^solr-.*\.port$"`) do (
+    set SOME_SOLR_PORT=
+    For /F "Delims=" %%J In (%SOLR_TIP%\bin\%%i) do set SOME_SOLR_PORT=%%~J
+    if NOT "!SOME_SOLR_PORT!"=="" (
+      for /f "tokens=2,5" %%j in ('netstat -aon ^| find /i "listening" ^| find /i "!SOME_SOLR_PORT!"') do (
+        set DELETE_PORT=!SOME_SOLR_PORT!
+      )
+    )
+  )
+)
+if "!DELETE_PORT!"=="" (
+  set "SCRIPT_ERROR=Could not find a running Solr instance on this host! Please use the -p option to specify the port."
+  goto err
+)
+
+if "!DELETE_CONFIG!"=="" (
+  set DELETE_CONFIG=true
+)
+
+"%JAVA%" %SOLR_SSL_OPTS% -Dsolr.install.dir="%SOLR_TIP%" -Dlog4j.configuration="file:%DEFAULT_SERVER_DIR%\scripts\cloud-scripts\log4j.properties" ^
+-classpath "%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*;%DEFAULT_SERVER_DIR%\lib\ext\*" ^
+org.apache.solr.util.SolrCLI delete -name !DELETE_NAME! -deleteConfig !DELETE_CONFIG! ^
+-solrUrl !SOLR_URL_SCHEME!://localhost:!DELETE_PORT!/solr
+
+goto done
+
+
 :invalid_cmd_line
 @echo.
 IF "!SCRIPT_ERROR!"=="" (

Modified: lucene/dev/branches/lucene_solr_5_0/solr/core/src/java/org/apache/solr/util/SolrCLI.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_5_0/solr/core/src/java/org/apache/solr/util/SolrCLI.java?rev=1652348&r1=1652347&r2=1652348&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_5_0/solr/core/src/java/org/apache/solr/util/SolrCLI.java (original)
+++ lucene/dev/branches/lucene_solr_5_0/solr/core/src/java/org/apache/solr/util/SolrCLI.java Fri Jan 16 06:39:24 2015
@@ -244,6 +244,8 @@ public class SolrCLI {
       return new CreateCoreTool();
     else if ("create".equals(toolType))
       return new CreateTool();
+    else if ("delete".equals(toolType))
+      return new DeleteTool();
 
     // If you add a built-in tool to this class, add it here to avoid
     // classpath scanning
@@ -265,6 +267,7 @@ public class SolrCLI {
     formatter.printHelp("create_collection", getToolOptions(new CreateCollectionTool()));
     formatter.printHelp("create_core", getToolOptions(new CreateCoreTool()));
     formatter.printHelp("create", getToolOptions(new CreateTool()));
+    formatter.printHelp("delete", getToolOptions(new DeleteTool()));
 
     List<Class<Tool>> toolClasses = findToolClassesInPackage("org.apache.solr.util");
     for (Class<Tool> next : toolClasses) {
@@ -1080,6 +1083,43 @@ public class SolrCLI {
             .create("configsetsDir")
   };
 
+  public static String getZkHost(CommandLine cli) throws Exception {
+    String zkHost = cli.getOptionValue("zkHost");
+    if (zkHost != null)
+      return zkHost;
+
+    // find it using the localPort
+    String solrUrl = cli.getOptionValue("solrUrl");
+    if (solrUrl == null)
+      throw new IllegalStateException(
+          "Must provide either the -zkHost or -solrUrl parameters to use the create_collection command!");
+
+    if (!solrUrl.endsWith("/"))
+      solrUrl += "/";
+
+    String systemInfoUrl = solrUrl+"admin/info/system";
+    CloseableHttpClient httpClient = getHttpClient();
+    try {
+      // hit Solr to get system info
+      Map<String,Object> systemInfo = getJson(httpClient, systemInfoUrl, 2);
+
+      // convert raw JSON into user-friendly output
+      StatusTool statusTool = new StatusTool();
+      Map<String,Object> status = statusTool.reportStatus(solrUrl, systemInfo, httpClient);
+      Map<String,Object> cloud = (Map<String, Object>)status.get("cloud");
+      if (cloud != null) {
+        String zookeeper = (String) cloud.get("ZooKeeper");
+        if (zookeeper.endsWith("(embedded)")) {
+          zookeeper = zookeeper.substring(0, zookeeper.length() - "(embedded)".length());
+        }
+        zkHost = zookeeper;
+      }
+    } finally {
+      HttpClientUtil.close(httpClient);
+    }
+
+    return zkHost;
+  }
 
   /**
    * Supports create_collection command in the bin/solr script.
@@ -1103,43 +1143,12 @@ public class SolrCLI {
       LogManager.getLogger("org.apache.zookeeper").setLevel(Level.ERROR);
       LogManager.getLogger("org.apache.solr.common.cloud").setLevel(Level.WARN);
 
-      String zkHost = cli.getOptionValue("zkHost");
+      String zkHost = getZkHost(cli);
       if (zkHost == null) {
-        // find it using the localPort
-        String solrUrl = cli.getOptionValue("solrUrl");
-        if (solrUrl == null)
-          throw new IllegalStateException(
-              "Must provide either the -zkHost or -solrUrl parameters to use the create_collection command!");
-
-        if (!solrUrl.endsWith("/"))
-          solrUrl += "/";
-
-        String systemInfoUrl = solrUrl+"admin/info/system";
-        CloseableHttpClient httpClient = getHttpClient();
-        try {
-          // hit Solr to get system info
-          Map<String,Object> systemInfo = getJson(httpClient, systemInfoUrl, 2);
-
-          // convert raw JSON into user-friendly output
-          StatusTool statusTool = new StatusTool();
-          Map<String,Object> status = statusTool.reportStatus(solrUrl, systemInfo, httpClient);
-
-          Map<String,Object> cloud = (Map<String, Object>)status.get("cloud");
-          if (cloud == null) {
-            System.err.println("\nERROR: Solr at "+solrUrl+
-                " is running in standalone server mode, please use the create_core command instead;\n" +
-                "create_collection can only be used when running in SolrCloud mode.\n");
-            return 1;
-          }
-
-          String zookeeper = (String) cloud.get("ZooKeeper");
-          if (zookeeper.endsWith("(embedded)")) {
-            zookeeper = zookeeper.substring(0,zookeeper.length()-"(embedded)".length());
-          }
-          zkHost = zookeeper;
-        } finally {
-          HttpClientUtil.close(httpClient);
-        }
+        System.err.println("\nERROR: Solr at "+cli.getOptionValue("solrUrl")+
+            " is running in standalone server mode, please use the create_core command instead;\n" +
+            "create_collection can only be used when running in SolrCloud mode.\n");
+        return 1;
       }
 
       int toolExitStatus = 0;
@@ -1502,4 +1511,220 @@ public class SolrCLI {
 
   } // end CreateTool class
 
+  public static class DeleteTool implements Tool {
+
+    @Override
+    public String getName() {
+      return "delete";
+    }
+
+    @SuppressWarnings("static-access")
+    @Override
+    public Option[] getOptions() {
+      return new Option[]{
+          OptionBuilder
+              .withArgName("URL")
+              .hasArg()
+              .isRequired(false)
+              .withDescription("Base Solr URL, default is http://localhost:8983/solr")
+              .create("solrUrl"),
+          OptionBuilder
+              .withArgName("NAME")
+              .hasArg()
+              .isRequired(true)
+              .withDescription("Name of the core / collection to delete.")
+              .create("name"),
+          OptionBuilder
+              .withArgName("true|false")
+              .hasArg()
+              .isRequired(false)
+              .withDescription("Flag to indicate if the underlying configuration directory for a collection should also be deleted; default is true")
+              .create("deleteConfig"),
+          OptionBuilder
+              .isRequired(false)
+              .withDescription("Skip safety checks when deleting the configuration directory used by a collection")
+              .create("forceDeleteConfig"),
+          OptionBuilder
+              .withArgName("HOST")
+              .hasArg()
+              .isRequired(false)
+              .withDescription("Address of the Zookeeper ensemble; defaults to: "+ZK_HOST)
+              .create("zkHost")
+      };
+    }
+
+    @Override
+    public int runTool(CommandLine cli) throws Exception {
+
+      // quiet down the ZK logging for cli tools
+      LogManager.getLogger("org.apache.zookeeper").setLevel(Level.ERROR);
+      LogManager.getLogger("org.apache.solr.common.cloud").setLevel(Level.WARN);
+
+      String solrUrl = cli.getOptionValue("solrUrl", "http://localhost:8983/solr");
+      if (!solrUrl.endsWith("/"))
+        solrUrl += "/";
+
+      String systemInfoUrl = solrUrl+"admin/info/system";
+      CloseableHttpClient httpClient = getHttpClient();
+
+      int result = 0;
+      try {
+        Map<String,Object> systemInfo = getJson(httpClient, systemInfoUrl, 2);
+        if ("solrcloud".equals(systemInfo.get("mode"))) {
+          result = deleteCollection(cli);
+        } else {
+          result = deleteCore(cli, httpClient, solrUrl);
+        }
+      } finally {
+        closeHttpClient(httpClient);
+      }
+
+      return result;
+    }
+
+    protected int deleteCollection(CommandLine cli) throws Exception {
+
+      String zkHost = getZkHost(cli);
+
+      int toolExitStatus = 0;
+      CloudSolrClient cloudSolrServer = null;
+      try {
+        cloudSolrServer = new CloudSolrClient(zkHost);
+        System.out.println("Connecting to ZooKeeper at " + zkHost);
+        cloudSolrServer.connect();
+        toolExitStatus = deleteCollection(cloudSolrServer, cli);
+      } catch (Exception exc) {
+        // since this is a CLI, spare the user the stacktrace
+        String excMsg = exc.getMessage();
+        if (excMsg != null) {
+          System.err.println("\nERROR: "+excMsg+"\n");
+          toolExitStatus = 1;
+        } else {
+          throw exc;
+        }
+      } finally {
+        if (cloudSolrServer != null) {
+          try {
+            cloudSolrServer.shutdown();
+          } catch (Exception ignore) {}
+        }
+      }
+
+      return toolExitStatus;
+    }
+
+    protected int deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli) throws Exception {
+      Set<String> liveNodes = cloudSolrClient.getZkStateReader().getClusterState().getLiveNodes();
+      if (liveNodes.isEmpty())
+        throw new IllegalStateException("No live nodes found! Cannot delete a collection until " +
+            "there is at least 1 live node in the cluster.");
+      String firstLiveNode = liveNodes.iterator().next();
+      ZkStateReader zkStateReader = cloudSolrClient.getZkStateReader();
+      String baseUrl = zkStateReader.getBaseUrlForNodeName(firstLiveNode);
+
+      String collectionName = cli.getOptionValue("name");
+
+      if (!zkStateReader.getClusterState().hasCollection(collectionName)) {
+        System.err.println("\nERROR: Collection "+collectionName+" not found!");
+        System.err.println();
+        return 1;
+      }
+
+      String configName = zkStateReader.readConfigName(collectionName);
+      boolean deleteConfig = "true".equals(cli.getOptionValue("deleteConfig", "true"));
+      if (deleteConfig && configName != null) {
+        if (cli.hasOption("forceDeleteConfig")) {
+          log.warn("Skipping safety checks, configuration directory "+configName+" will be deleted with impunity.");
+        } else {
+          // need to scan all Collections to see if any are using the config
+          Set<String> collections = zkStateReader.getClusterState().getCollections();
+
+          // give a little note to the user if there are many collections in case it takes a while
+          if (collections.size() > 50)
+            log.info("Scanning " + collections.size() +
+                " to ensure no other collections are using config " + configName);
+
+          for (String next : collections) {
+            if (collectionName.equals(next))
+              continue; // don't check the collection we're deleting
+
+            if (configName.equals(zkStateReader.readConfigName(next))) {
+              deleteConfig = false;
+              log.warn("Configuration directory "+configName+" is also being used by "+next+
+                  "; configuration will not be deleted from ZooKeeper. You can pass the -forceDeleteConfig flag to force delete.");
+              break;
+            }
+          }
+        }
+      }
+
+      String deleteCollectionUrl =
+          String.format(Locale.ROOT,
+              "%s/admin/collections?action=DELETE&name=%s",
+              baseUrl,
+              collectionName);
+
+      System.out.println("\nDeleting collection '"+collectionName+"' using command:\n"+deleteCollectionUrl+"\n");
+
+      Map<String,Object> json = null;
+      try {
+        json = getJson(deleteCollectionUrl);
+      } catch (SolrServerException sse) {
+        System.err.println("Failed to delete collection '"+collectionName+"' due to: "+sse.getMessage());
+        System.err.println();
+        return 1;
+      }
+
+      if (deleteConfig) {
+        String configZnode = "/configs/" + configName;
+        try {
+          zkStateReader.getZkClient().clean(configZnode);
+        } catch (Exception exc) {
+          System.err.println("\nERROR: Failed to delete configuration directory "+configZnode+" in ZooKeeper due to: "+
+            exc.getMessage()+"\nYou'll need to manually delete this znode using the zkcli script.");
+        }
+      }
+
+      if (json != null) {
+        CharArr arr = new CharArr();
+        new JSONWriter(arr, 2).write(json);
+        System.out.println(arr.toString());
+        System.out.println();
+      }
+
+      return 0;
+    }
+
+    protected int deleteCore(CommandLine cli, CloseableHttpClient httpClient, String solrUrl) throws Exception {
+
+      int status = 0;
+      String coreName = cli.getOptionValue("name");
+      String deleteCoreUrl =
+          String.format(Locale.ROOT,
+              "%sadmin/cores?action=UNLOAD&core=%s&deleteIndex=true&deleteDataDir=true&deleteInstanceDir=true",
+              solrUrl,
+              coreName);
+
+      System.out.println("\nDeleting core '"+coreName+"' using command:\n"+deleteCoreUrl+"\n");
+
+      Map<String,Object> json = null;
+      try {
+        json = getJson(deleteCoreUrl);
+      } catch (SolrServerException sse) {
+        System.err.println("Failed to delete core '"+coreName+"' due to: "+sse.getMessage());
+        System.err.println();
+        status = 1;
+      }
+
+      if (json != null) {
+        CharArr arr = new CharArr();
+        new JSONWriter(arr, 2).write(json);
+        System.out.println(arr.toString());
+        System.out.println();
+      }
+
+      return status;
+    }
+
+  } // end DeleteTool class
 }