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

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


The following commit(s) were added to refs/heads/master by this push:
     new 1474e46  HDDS-5291. Handle SIGTERM to ensure clean shutdown of 
OM/DN/SCM (#2301)
1474e46 is described below

commit 1474e4651f7caa8fe488982841be712b91c5f6da
Author: Bharat Viswanadham <[email protected]>
AuthorDate: Fri Jul 16 09:29:13 2021 +0530

    HDDS-5291. Handle SIGTERM to ensure clean shutdown of OM/DN/SCM (#2301)
---
 .../java/org/apache/hadoop/hdds/HddsUtils.java     |   9 +
 .../hadoop/ozone/conf/OzoneServiceConfig.java      |  76 ++++
 .../org/apache/hadoop/ozone/conf/package-info.java |  22 ++
 .../hadoop/ozone/util/ShutdownHookManager.java     | 391 +++++++++++++++++++++
 .../org/apache/hadoop/ozone/util/package-info.java |  22 ++
 .../apache/hadoop/ozone/HddsDatanodeService.java   |  13 +-
 .../transport/server/ratis/XceiverServerRatis.java |   2 +-
 .../container/common/volume/MutableVolumeSet.java  |  15 +-
 .../hdds/scm/server/StorageContainerManager.java   |   2 +
 .../scm/server/StorageContainerManagerStarter.java |  12 +-
 .../dist/src/shell/ozone/ozone-functions.sh        |   2 +-
 .../apache/hadoop/ozone/insight/LogSubcommand.java |   9 +-
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |   8 +-
 .../hadoop/ozone/om/OzoneManagerStarter.java       |  12 +-
 .../org/apache/hadoop/ozone/recon/ReconServer.java |   6 +-
 .../java/org/apache/hadoop/ozone/s3/Gateway.java   |  11 +
 .../hadoop/ozone/freon/BaseFreonGenerator.java     |   7 +-
 .../hadoop/ozone/freon/RandomKeyGenerator.java     |  16 +-
 18 files changed, 604 insertions(+), 31 deletions(-)

diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
index c38ff3d..a62fd6d 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
@@ -65,6 +65,7 @@ import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DATANODE_PORT_D
 import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DATANODE_PORT_KEY;
 import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_NAMES;
 
+import org.apache.hadoop.ozone.conf.OzoneServiceConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -603,4 +604,12 @@ public final class HddsUtils {
     }
     return sb.toString();
   }
+
+  /**
+   * Return Ozone service shutdown time out.
+   * @param conf
+   */
+  public static long getShutDownTimeOut(ConfigurationSource conf) {
+    return 
conf.getObject(OzoneServiceConfig.class).getServiceShutdownTimeout();
+  }
 }
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/conf/OzoneServiceConfig.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/conf/OzoneServiceConfig.java
new file mode 100644
index 0000000..58e88d3
--- /dev/null
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/conf/OzoneServiceConfig.java
@@ -0,0 +1,76 @@
+/*
+ * 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.hadoop.ozone.conf;
+
+import org.apache.hadoop.hdds.conf.Config;
+import org.apache.hadoop.hdds.conf.ConfigGroup;
+import org.apache.hadoop.hdds.conf.ConfigType;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.hadoop.hdds.conf.ConfigTag.DATANODE;
+import static org.apache.hadoop.hdds.conf.ConfigTag.OM;
+import static org.apache.hadoop.hdds.conf.ConfigTag.OZONE;
+import static org.apache.hadoop.hdds.conf.ConfigTag.RECON;
+import static org.apache.hadoop.hdds.conf.ConfigTag.S3GATEWAY;
+import static org.apache.hadoop.hdds.conf.ConfigTag.SCM;
+
+/**
+ * This class is used to define Ozone service level configs which are needed
+ * for all the ozone services.
+ */
+@ConfigGroup(prefix = "ozone.service")
+public class OzoneServiceConfig {
+
+  /** Minimum shutdown timeout: {@value} second(s). */
+  public static final long OZONE_SHUTDOWN_TIMEOUT_MINIMUM = 1;
+
+  /** The default time unit used: seconds. */
+  public static final TimeUnit OZONE_SHUTDOWN_TIME_UNIT_DEFAULT =
+          TimeUnit.SECONDS;
+
+  public static final int DEFAULT_SHUTDOWN_HOOK_PRIORITY = 10;
+
+  public static final String SERVICE_SHUTDOWN_TIMEOUT =
+      "shutdown.timeout";
+  /** Default shutdown hook timeout: {@value} seconds. */
+  public static final String SERVICE_SHUTDOWN_TIMEOUT_DEFAULT = "60s";
+
+  @Config(key = SERVICE_SHUTDOWN_TIMEOUT,
+      defaultValue = SERVICE_SHUTDOWN_TIMEOUT_DEFAULT,
+      type = ConfigType.TIME,
+      tags = {OZONE, OM, SCM, DATANODE, RECON, S3GATEWAY},
+      timeUnit = TimeUnit.SECONDS,
+      description = "Timeout to wait for each shutdown operation to complete" +
+          "If a hook takes longer than this time to complete, it will " +
+          "be interrupted, so the service will shutdown. This allows the " +
+          "service shutdown to recover from a blocked operation. " +
+          "The minimum duration of the timeout is 1 second, if " +
+          "hook has been configured with a timeout less than 1 second."
+  )
+  private long serviceShutdownTimeout = 60;
+
+  public long getServiceShutdownTimeout() {
+    return serviceShutdownTimeout;
+  }
+
+  public void setServiceShutdownTimeout(long serviceShutdownTimeout) {
+    this.serviceShutdownTimeout = serviceShutdownTimeout;
+  }
+}
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/conf/package-info.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/conf/package-info.java
new file mode 100644
index 0000000..cb6ec51
--- /dev/null
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/conf/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Configuration classes for Ozone.
+ */
+package org.apache.hadoop.ozone.conf;
\ No newline at end of file
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/util/ShutdownHookManager.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/util/ShutdownHookManager.java
new file mode 100644
index 0000000..05dc688
--- /dev/null
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/util/ShutdownHookManager.java
@@ -0,0 +1,391 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.ozone.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.apache.hadoop.hdds.HddsUtils;
+import org.apache.hadoop.hdds.conf.ConfigurationSource;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.OZONE_SHUTDOWN_TIMEOUT_MINIMUM;
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.OZONE_SHUTDOWN_TIME_UNIT_DEFAULT;
+
+/**
+ * The <code>ShutdownHookManager</code> enables running shutdownHook
+ * in a deterministic order, higher priority first.
+ * <p/>
+ * The JVM runs ShutdownHooks in a non-deterministic order or in parallel.
+ * This class registers a single JVM shutdownHook and run all the
+ * shutdownHooks registered to it (to this class) in order based on their
+ * priority.
+ *
+ * Unless a hook was registered with a shutdown explicitly set through
+ * {@link #addShutdownHook(Runnable, int, long, TimeUnit)},
+ * the shutdown time allocated to it is set by the configuration option
+ * {@link org.apache.hadoop.ozone.conf.OzoneServiceConfig
+ * #SERVICE_SHUTDOWN_TIMEOUT}
+ * {@code ozone-site.xml}, with a default value of
+ * {@link org.apache.hadoop.ozone.conf.OzoneServiceConfig
+ * #SERVICE_SHUTDOWN_TIMEOUT_DEFAULT}
+ * seconds.
+ *
+ * This code is taken from hadoop project.
+ * 1. This is to avoid dependency on hadoop.
+ * 2. To use a ozone specific config and defaults.
+ * 3. Now any fix happened to this class in hadoop for this class, we can
+ * backport this to this with out waiting for a new hadoop release.
+ *
+ */
[email protected]
[email protected]
+public final class ShutdownHookManager {
+
+  private static final ShutdownHookManager MGR = new ShutdownHookManager();
+
+  private static final Logger LOG =
+      LoggerFactory.getLogger(ShutdownHookManager.class);
+
+
+
+  private static final ExecutorService EXECUTOR =
+      Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
+          .setDaemon(true)
+          .setNameFormat("shutdown-hook-%01d")
+          .build());
+
+  static {
+    try {
+      Runtime.getRuntime().addShutdownHook(
+          new Thread() {
+            @Override
+            public void run() {
+              if (MGR.shutdownInProgress.getAndSet(true)) {
+                LOG.info("Shutdown process invoked a second time: ignoring");
+                return;
+              }
+              long started = System.currentTimeMillis();
+              int timeoutCount = MGR.executeShutdown();
+              long ended = System.currentTimeMillis();
+              LOG.debug(String.format(
+                  "Completed shutdown in %.3f seconds; Timeouts: %d",
+                  (ended-started)/1000.0, timeoutCount));
+              // each of the hooks have executed; now shut down the
+              // executor itself.
+              shutdownExecutor(new OzoneConfiguration());
+            }
+          }
+      );
+    } catch (IllegalStateException ex) {
+      // JVM is being shut down. Ignore
+      LOG.warn("Failed to add the ShutdownHook", ex);
+    }
+  }
+
+  /**
+   * Execute the shutdown.
+   * This is exposed purely for testing: do not invoke it.
+   * @return the number of shutdown hooks which timed out.
+   */
+  @InterfaceAudience.Private
+  @VisibleForTesting
+  int executeShutdown() {
+    int timeouts = 0;
+    for (HookEntry entry: getShutdownHooksInOrder()) {
+      Future<?> future = EXECUTOR.submit(entry.getHook());
+      try {
+        future.get(entry.getTimeout(), entry.getTimeUnit());
+      } catch (TimeoutException ex) {
+        timeouts++;
+        future.cancel(true);
+        LOG.warn("ShutdownHook '" + entry.getHook().getClass().
+            getSimpleName() + "' timeout, " + ex.toString(), ex);
+      } catch (Throwable ex) {
+        LOG.warn("ShutdownHook '" + entry.getHook().getClass().
+            getSimpleName() + "' failed, " + ex.toString(), ex);
+      }
+    }
+    return timeouts;
+  }
+
+  /**
+   * Shutdown the executor thread itself.
+   * @param conf the configuration containing the shutdown timeout setting.
+   */
+  private static void shutdownExecutor(final ConfigurationSource conf) {
+    try {
+      EXECUTOR.shutdown();
+      long shutdownTimeout = getShutdownTimeout(conf);
+      if (!EXECUTOR.awaitTermination(
+          shutdownTimeout, OZONE_SHUTDOWN_TIME_UNIT_DEFAULT)) {
+        // timeout waiting for the
+        LOG.error("ShutdownHookManger shutdown forcefully after"
+            + " {} seconds.", shutdownTimeout);
+        EXECUTOR.shutdownNow();
+      }
+      LOG.debug("ShutdownHookManger completed shutdown.");
+    } catch (InterruptedException ex) {
+      // interrupted.
+      LOG.error("ShutdownHookManger interrupted while waiting for " +
+          "termination.", ex);
+      EXECUTOR.shutdownNow();
+      Thread.currentThread().interrupt();
+    }
+  }
+
+  /**
+   * Return <code>ShutdownHookManager</code> singleton.
+   *
+   * @return <code>ShutdownHookManager</code> singleton.
+   */
+  @InterfaceAudience.Public
+  public static ShutdownHookManager get() {
+    return MGR;
+  }
+
+  /**
+   * Get the shutdown timeout in seconds, from the supplied
+   * configuration.
+   * @param conf configuration to use.
+   * @return a timeout, always greater than or equal to
+   * {@link org.apache.hadoop.ozone.conf.OzoneServiceConfig
+   * #OZONE_SHUTDOWN_TIMEOUT_MINIMUM}
+   */
+  @InterfaceAudience.Private
+  @VisibleForTesting
+  static long getShutdownTimeout(ConfigurationSource conf) {
+    long duration = HddsUtils.getShutDownTimeOut(conf);
+    if (duration < OZONE_SHUTDOWN_TIMEOUT_MINIMUM) {
+      duration = OZONE_SHUTDOWN_TIMEOUT_MINIMUM;
+    }
+    return duration;
+  }
+
+  /**
+   * Private structure to store ShutdownHook, its priority and timeout
+   * settings.
+   */
+  @InterfaceAudience.Private
+  @VisibleForTesting
+  static class HookEntry {
+    private final Runnable hook;
+    private final int priority;
+    private final long timeout;
+    private final TimeUnit unit;
+
+    HookEntry(Runnable hook, int priority) {
+      this(hook, priority, getShutdownTimeout(new OzoneConfiguration()),
+              OZONE_SHUTDOWN_TIME_UNIT_DEFAULT);
+    }
+
+    HookEntry(Runnable hook, int priority, long timeout, TimeUnit unit) {
+      this.hook = hook;
+      this.priority = priority;
+      this.timeout = timeout;
+      this.unit = unit;
+    }
+
+    @Override
+    public int hashCode() {
+      return hook.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      boolean eq = false;
+      if (obj != null) {
+        if (obj instanceof HookEntry) {
+          eq = (hook == ((HookEntry)obj).hook);
+        }
+      }
+      return eq;
+    }
+
+    Runnable getHook() {
+      return hook;
+    }
+
+    int getPriority() {
+      return priority;
+    }
+
+    long getTimeout() {
+      return timeout;
+    }
+
+    TimeUnit getTimeUnit() {
+      return unit;
+    }
+  }
+
+  private final Set<HookEntry> hooks =
+      Collections.synchronizedSet(new HashSet<>());
+
+  private AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
+
+  //private to constructor to ensure singularity
+  @VisibleForTesting
+  @InterfaceAudience.Private
+  ShutdownHookManager() {
+  }
+
+  /**
+   * Returns the list of shutdownHooks in order of execution,
+   * Highest priority first.
+   *
+   * @return the list of shutdownHooks in order of execution.
+   */
+  @InterfaceAudience.Private
+  @VisibleForTesting
+  List<HookEntry > getShutdownHooksInOrder() {
+    List<HookEntry > list;
+    synchronized (hooks) {
+      list = new ArrayList<HookEntry>(hooks);
+    }
+    Collections.sort(list, new Comparator< HookEntry >() {
+
+      //reversing comparison so highest priority hooks are first
+      @Override
+      public int compare(HookEntry o1, HookEntry o2) {
+        return o2.priority - o1.priority;
+      }
+    });
+    return list;
+  }
+
+  /**
+   * Adds a shutdownHook with a priority, the higher the priority
+   * the earlier will run. ShutdownHooks with same priority run
+   * in a non-deterministic order.
+   *
+   * @param shutdownHook shutdownHook <code>Runnable</code>
+   * @param priority priority of the shutdownHook.
+   */
+  @InterfaceAudience.Public
+  @InterfaceStability.Stable
+  public void addShutdownHook(Runnable shutdownHook, int priority) {
+    if (shutdownHook == null) {
+      throw new IllegalArgumentException("shutdownHook cannot be NULL");
+    }
+    if (shutdownInProgress.get()) {
+      throw new IllegalStateException("Shutdown in progress, cannot add a " +
+          "shutdownHook");
+    }
+    hooks.add(new HookEntry(shutdownHook, priority));
+  }
+
+  /**
+   *
+   * Adds a shutdownHook with a priority and timeout the higher the priority
+   * the earlier will run. ShutdownHooks with same priority run
+   * in a non-deterministic order. The shutdown hook will be terminated if it
+   * has not been finished in the specified period of time.
+   *
+   * @param shutdownHook shutdownHook <code>Runnable</code>
+   * @param priority priority of the shutdownHook
+   * @param timeout timeout of the shutdownHook
+   * @param unit unit of the timeout <code>TimeUnit</code>
+   */
+  @InterfaceAudience.Public
+  @InterfaceStability.Stable
+  public void addShutdownHook(Runnable shutdownHook, int priority, long 
timeout,
+      TimeUnit unit) {
+    if (shutdownHook == null) {
+      throw new IllegalArgumentException("shutdownHook cannot be NULL");
+    }
+    if (shutdownInProgress.get()) {
+      throw new IllegalStateException("Shutdown in progress, cannot add a " +
+          "shutdownHook");
+    }
+    hooks.add(new HookEntry(shutdownHook, priority, timeout, unit));
+  }
+
+  /**
+   * Removes a shutdownHook.
+   *
+   * @param shutdownHook shutdownHook to remove.
+   * @return TRUE if the shutdownHook was registered and removed,
+   * FALSE otherwise.
+   */
+  @InterfaceAudience.Public
+  @InterfaceStability.Stable
+  public boolean removeShutdownHook(Runnable shutdownHook) {
+    if (shutdownInProgress.get()) {
+      throw new IllegalStateException("Shutdown in progress, cannot remove a " 
+
+          "shutdownHook");
+    }
+    // hooks are only == by runnable
+    return hooks.remove(new HookEntry(shutdownHook, 0,
+            OZONE_SHUTDOWN_TIMEOUT_MINIMUM,
+            OZONE_SHUTDOWN_TIME_UNIT_DEFAULT));
+  }
+
+  /**
+   * Indicates if a shutdownHook is registered or not.
+   *
+   * @param shutdownHook shutdownHook to check if registered.
+   * @return TRUE/FALSE depending if the shutdownHook is is registered.
+   */
+  @InterfaceAudience.Public
+  @InterfaceStability.Stable
+  public boolean hasShutdownHook(Runnable shutdownHook) {
+    return hooks.contains(new HookEntry(shutdownHook, 0,
+            OZONE_SHUTDOWN_TIMEOUT_MINIMUM,
+            OZONE_SHUTDOWN_TIME_UNIT_DEFAULT));
+  }
+
+  /**
+   * Indicates if shutdown is in progress or not.
+   *
+   * @return TRUE if the shutdown is in progress, otherwise FALSE.
+   */
+  @InterfaceAudience.Public
+  @InterfaceStability.Stable
+  public boolean isShutdownInProgress() {
+    return shutdownInProgress.get();
+  }
+
+  /**
+   * clear all registered shutdownHooks.
+   */
+  @InterfaceAudience.Public
+  @InterfaceStability.Stable
+  public void clearShutdownHooks() {
+    hooks.clear();
+  }
+}
+
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/util/package-info.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/util/package-info.java
new file mode 100644
index 0000000..92de1ec
--- /dev/null
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Utility classes required for ozone.
+ */
+package org.apache.hadoop.ozone.util;
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
index 27a0dc8..56003e1 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
@@ -62,6 +62,7 @@ import 
org.apache.hadoop.ozone.container.common.utils.HddsVolumeUtil;
 import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
 import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
 import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import 
org.apache.hadoop.security.authentication.client.AuthenticationException;
@@ -71,11 +72,14 @@ import org.apache.hadoop.util.Time;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.sun.jmx.mbeanserver.Introspector;
+
 import static 
org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec.getX509Certificate;
 import static 
org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest.getEncodedString;
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_DATANODE_PLUGINS_KEY;
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
 import static org.apache.hadoop.ozone.common.Storage.StorageState.INITIALIZED;
 import static org.apache.hadoop.util.ExitUtil.terminate;
+
 import org.bouncycastle.pkcs.PKCS10CertificationRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -169,7 +173,14 @@ public class HddsDatanodeService extends GenericCli 
implements ServicePlugin {
           HddsDatanodeService.class, args, LOG);
     }
     start(createOzoneConfiguration());
-    join();
+    ShutdownHookManager.get().addShutdownHook(() -> {
+      try {
+        stop();
+        join();
+      } catch (Exception e) {
+        LOG.error("Error during stop Ozone Datanode.", e);
+      }
+    }, DEFAULT_SHUTDOWN_HOOK_PRIORITY);
     return null;
   }
 
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/XceiverServerRatis.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/XceiverServerRatis.java
index 867127e..cca1c08 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/XceiverServerRatis.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/XceiverServerRatis.java
@@ -513,7 +513,7 @@ public final class XceiverServerRatis implements 
XceiverServerSpi {
         }
         isStarted = false;
       } catch (IOException e) {
-        throw new RuntimeException(e);
+        LOG.error("XceiverServerRatis Could not be stopped gracefully.", e);
       }
     }
   }
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
index 76a576b..47fe4e3 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
@@ -39,13 +39,13 @@ import 
org.apache.hadoop.ozone.container.common.statemachine.DatanodeConfigurati
 import org.apache.hadoop.ozone.container.common.statemachine.StateContext;
 import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
 import org.apache.hadoop.util.ShutdownHookManager;
+import org.apache.hadoop.util.Timer;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import static org.apache.hadoop.util.RunJar.SHUTDOWN_HOOK_PRIORITY;
 
-import org.apache.hadoop.util.Timer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -415,15 +415,17 @@ public class MutableVolumeSet implements VolumeSet {
    * This method, call shutdown on each volume to shutdown volume usage
    * thread and write scmUsed on each volume.
    */
-  private void saveVolumeSetUsed() {
-    for (StorageVolume volume : volumeMap.values()) {
+
+  private synchronized void saveVolumeSetUsed() {
+    for (StorageVolume hddsVolume : volumeMap.values()) {
       try {
-        volume.shutdown();
+        hddsVolume.shutdown();
       } catch (Exception ex) {
-        LOG.error("Failed to shutdown volume : " + volume.getStorageDir(),
+        LOG.error("Failed to shutdown volume : " + hddsVolume.getStorageDir(),
             ex);
       }
     }
+    volumeMap.clear();
   }
 
   /**
@@ -431,9 +433,6 @@ public class MutableVolumeSet implements VolumeSet {
    */
   public void shutdown() {
     saveVolumeSetUsed();
-    if (shutdownHook != null) {
-      ShutdownHookManager.get().removeShutdownHook(shutdownHook);
-    }
   }
 
   @Override
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
index ad011e7..c739757 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
@@ -1423,6 +1423,7 @@ public final class StorageContainerManager extends 
ServiceRuntimeInfoImpl
     }
 
     try {
+      LOG.info("Stopping SCM HA services.");
       scmHAManager.shutdown();
     } catch (Exception ex) {
       LOG.error("SCM HA Manager stop failed", ex);
@@ -1432,6 +1433,7 @@ public final class StorageContainerManager extends 
ServiceRuntimeInfoImpl
     IOUtils.cleanupWithLogger(LOG, pipelineManager);
 
     try {
+      LOG.info("Stopping SCM MetadataStore.");
       scmMetadataStore.stop();
     } catch (Exception ex) {
       LOG.error("SCM Metadata store stop failed", ex);
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManagerStarter.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManagerStarter.java
index 8960269..1d8859f 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManagerStarter.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManagerStarter.java
@@ -28,6 +28,7 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.tracing.TracingUtil;
 import org.apache.hadoop.hdds.utils.HddsVersionInfo;
 import org.apache.hadoop.ozone.common.StorageInfo;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import 
org.apache.hadoop.security.authentication.client.AuthenticationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,6 +37,8 @@ import picocli.CommandLine.Command;
 
 import java.io.IOException;
 
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
+
 /**
  * This class provides a command line interface to start the SCM
  * using Picocli.
@@ -164,7 +167,14 @@ public class StorageContainerManagerStarter extends 
GenericCli {
     public void start(OzoneConfiguration conf) throws Exception {
       StorageContainerManager stm = StorageContainerManager.createSCM(conf);
       stm.start();
-      stm.join();
+      ShutdownHookManager.get().addShutdownHook(() -> {
+        try {
+          stm.stop();
+          stm.join();
+        } catch (Exception e) {
+          LOG.error("Error during stop StorageContainerManager", e);
+        }
+      }, DEFAULT_SHUTDOWN_HOOK_PRIORITY);
     }
 
     @Override
diff --git a/hadoop-ozone/dist/src/shell/ozone/ozone-functions.sh 
b/hadoop-ozone/dist/src/shell/ozone/ozone-functions.sh
index 886b084..91ede8d 100755
--- a/hadoop-ozone/dist/src/shell/ozone/ozone-functions.sh
+++ b/hadoop-ozone/dist/src/shell/ozone/ozone-functions.sh
@@ -848,7 +848,7 @@ function ozone_basic_init
   OZONE_LOGFILE=${OZONE_LOGFILE:-ozone.log}
   OZONE_LOGLEVEL=${OZONE_LOGLEVEL:-INFO}
   OZONE_NICENESS=${OZONE_NICENESS:-0}
-  OZONE_STOP_TIMEOUT=${OZONE_STOP_TIMEOUT:-5}
+  OZONE_STOP_TIMEOUT=${OZONE_STOP_TIMEOUT:-60}
   OZONE_PID_DIR=${OZONE_PID_DIR:-/tmp}
   OZONE_ROOT_LOGGER=${OZONE_ROOT_LOGGER:-${OZONE_LOGLEVEL},console}
   OZONE_DAEMON_ROOT_LOGGER=${OZONE_DAEMON_ROOT_LOGGER:-${OZONE_LOGLEVEL},RFA}
diff --git 
a/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/LogSubcommand.java
 
b/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/LogSubcommand.java
index 5e487a5..1bae30b 100644
--- 
a/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/LogSubcommand.java
+++ 
b/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/LogSubcommand.java
@@ -36,12 +36,15 @@ import org.apache.hadoop.hdds.cli.HddsVersionProvider;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.ozone.insight.LoggerSource.Level;
 
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.HttpClientBuilder;
 import picocli.CommandLine;
 
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
+
 /**
  * Subcommand to display log.
  */
@@ -76,9 +79,9 @@ public class LogSubcommand extends BaseInsightSubCommand
     List<LoggerSource> loggers = insight.getRelatedLoggers(verbose, filters);
 
     setLogLevels(conf, loggers, LoggerSource::getLevel);
-    Runtime.getRuntime().addShutdownHook(new Thread(() ->
-        setLogLevels(conf, loggers, any -> Level.INFO)
-    ));
+    ShutdownHookManager.get().addShutdownHook(() ->
+        setLogLevels(conf, loggers, any -> Level.INFO),
+            DEFAULT_SHUTDOWN_HOOK_PRIORITY);
 
     Set<Component> sources = loggers.stream().map(LoggerSource::getComponent)
         .collect(Collectors.toSet());
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 19aee4c..82eb2f8 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -169,6 +169,7 @@ import org.apache.hadoop.ozone.upgrade.UpgradeFinalizer;
 import org.apache.hadoop.ozone.upgrade.UpgradeFinalizer.StatusAndMessages;
 import org.apache.hadoop.hdds.ExitManager;
 import org.apache.hadoop.ozone.util.OzoneVersionInfo;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
@@ -178,7 +179,6 @@ import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.JvmPauseMonitor;
 import org.apache.hadoop.util.KMSUtil;
 import org.apache.hadoop.util.ReflectionUtils;
-import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.Time;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -705,8 +705,10 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
   private void saveOmMetrics() {
     try {
       boolean success;
-      Files.createDirectories(
-          getTempMetricsStorageFile().getParentFile().toPath());
+      File parent = getTempMetricsStorageFile().getParentFile();
+      if (!parent.exists()) {
+        Files.createDirectories(parent.toPath());
+      }
       try (BufferedWriter writer = new BufferedWriter(
           new OutputStreamWriter(new FileOutputStream(
               getTempMetricsStorageFile()), StandardCharsets.UTF_8))) {
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java
index d6a9550..39069a8 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java
@@ -23,6 +23,7 @@ import org.apache.hadoop.hdds.cli.HddsVersionProvider;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.tracing.TracingUtil;
 import org.apache.hadoop.ozone.util.OzoneVersionInfo;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import 
org.apache.hadoop.security.authentication.client.AuthenticationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,6 +33,8 @@ import picocli.CommandLine.Command;
 
 import java.io.IOException;
 
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
+
 /**
  * This class provides a command line interface to start the OM
  * using Picocli.
@@ -146,7 +149,14 @@ public class OzoneManagerStarter extends GenericCli {
         AuthenticationException {
       OzoneManager om = OzoneManager.createOm(conf);
       om.start();
-      om.join();
+      ShutdownHookManager.get().addShutdownHook(() -> {
+        try {
+          om.stop();
+          om.join();
+        } catch (Exception e) {
+          LOG.error("Error during stop OzoneManager.", e);
+        }
+      }, DEFAULT_SHUTDOWN_HOOK_PRIORITY);
     }
 
     @Override
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
index c9030c0..d670a6d 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.ozone.recon;
 
 import static 
org.apache.hadoop.hdds.recon.ReconConfig.ConfigStrings.OZONE_RECON_KERBEROS_KEYTAB_FILE_KEY;
 import static 
org.apache.hadoop.hdds.recon.ReconConfig.ConfigStrings.OZONE_RECON_KERBEROS_PRINCIPAL_KEY;
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
 
 import org.apache.hadoop.hdds.HddsUtils;
 import org.apache.hadoop.hdds.StringUtils;
@@ -35,6 +36,7 @@ import 
org.apache.hadoop.ozone.recon.spi.ReconNamespaceSummaryManager;
 import org.apache.hadoop.ozone.recon.spi.StorageContainerServiceProvider;
 import org.apache.hadoop.ozone.recon.spi.impl.ReconDBProvider;
 import org.apache.hadoop.ozone.util.OzoneVersionInfo;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import 
org.apache.hadoop.security.authentication.client.AuthenticationException;
@@ -122,14 +124,14 @@ public class ReconServer extends GenericCli {
     start();
     isStarted = true;
 
-    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+    ShutdownHookManager.get().addShutdownHook(() -> {
       try {
         stop();
         join();
       } catch (Exception e) {
         LOG.error("Error during stop Recon server", e);
       }
-    }));
+    }, DEFAULT_SHUTDOWN_HOOK_PRIORITY);
     return null;
   }
 
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
index 459a7a4..8b5eddb 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
@@ -26,11 +26,14 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.tracing.TracingUtil;
 import org.apache.hadoop.ozone.util.OzoneVersionInfo;
 
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import picocli.CommandLine.Command;
 
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
+
 /**
  * This class is used to start/stop S3 compatible rest server.
  */
@@ -56,6 +59,14 @@ public class Gateway extends GenericCli {
     UserGroupInformation.setConfiguration(ozoneConfiguration);
     httpServer = new S3GatewayHttpServer(ozoneConfiguration, "s3gateway");
     start();
+
+    ShutdownHookManager.get().addShutdownHook(() -> {
+      try {
+        stop();
+      } catch (Exception e) {
+        LOG.error("Error during stop S3Gateway", e);
+      }
+    }, DEFAULT_SHUTDOWN_HOOK_PRIORITY);
     return null;
   }
 
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/BaseFreonGenerator.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/BaseFreonGenerator.java
index 0649a0c..0a639ec 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/BaseFreonGenerator.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/BaseFreonGenerator.java
@@ -43,6 +43,7 @@ import org.apache.hadoop.ozone.om.protocolPB.OmTransport;
 import org.apache.hadoop.ozone.om.protocolPB.OmTransportFactory;
 import 
org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolClientSideTranslatorPB;
 import org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolPB;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.hadoop.security.UserGroupInformation;
 
 import com.codahale.metrics.ConsoleReporter;
@@ -251,15 +252,15 @@ public class BaseFreonGenerator {
 
     pathSchema = new PathSchema(prefix);
 
-    Runtime.getRuntime().addShutdownHook(
-        new Thread(() -> {
+    ShutdownHookManager.get().addShutdownHook(
+        () -> {
           try {
             freonCommand.stopHttpServer();
           } catch (Exception ex) {
             LOG.error("HTTP server can't be stopped.", ex);
           }
           printReport();
-        }));
+        }, 10);
 
     executor = Executors.newFixedThreadPool(threadNo);
 
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/RandomKeyGenerator.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/RandomKeyGenerator.java
index a9f0f51..4b2e40d 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/RandomKeyGenerator.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/RandomKeyGenerator.java
@@ -53,6 +53,7 @@ import org.apache.hadoop.ozone.client.OzoneClientFactory;
 import org.apache.hadoop.ozone.client.OzoneVolume;
 import org.apache.hadoop.ozone.client.io.OzoneInputStream;
 import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
+import org.apache.hadoop.ozone.util.ShutdownHookManager;
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.VersionInfo;
 
@@ -74,6 +75,8 @@ import picocli.CommandLine.Command;
 import picocli.CommandLine.Option;
 import picocli.CommandLine.ParentCommand;
 
+import static 
org.apache.hadoop.ozone.conf.OzoneServiceConfig.DEFAULT_SHUTDOWN_HOOK_PRIORITY;
+
 /**
  * Data generator tool to generate as much keys as possible.
  */
@@ -381,13 +384,12 @@ public final class RandomKeyGenerator implements 
Callable<Void> {
    * Adds ShutdownHook to print statistics.
    */
   private void addShutdownHook() {
-    Runtime.getRuntime().addShutdownHook(
-        new Thread(() -> {
-          printStats(System.out);
-          if (freon != null) {
-            freon.stopHttpServer();
-          }
-        }));
+    ShutdownHookManager.get().addShutdownHook(() -> {
+      printStats(System.out);
+      if (freon != null) {
+        freon.stopHttpServer();
+      }
+    }, DEFAULT_SHUTDOWN_HOOK_PRIORITY);
   }
 
   private void doCleanObjects() throws InterruptedException {

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to