/*
 * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
 */

package io.airbyte.commons.lang;

import com.google.common.annotations.VisibleForTesting;
import java.io.Closeable;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Registers a shutdown hook that calls the close method of the provided objects. If an object does
 * not support either the {@link AutoCloseable} or {@link Closeable} interface, it will be ignored.
 *
 * This is a temporary class that is being provided to ensure that resources created by each
 * application are properly closed on shutdown. This logic will no longer be necessary once an
 * application framework is introduced to the project that can provide object lifecycle management.
 */
public class CloseableShutdownHook {

  private static final Logger LOGGER = LoggerFactory.getLogger(CloseableShutdownHook.class);

  /**
   * Registers a runtime shutdown hook with the application for each provided closeable object.
   *
   * @param objects An array of objects to be closed on application shutdown.
   */
  public static void registerRuntimeShutdownHook(final Object... objects) {
    Runtime.getRuntime().addShutdownHook(buildShutdownHookThread(objects));
  }

  /**
   * Builds the {@link Thread} that will be registered as an application shutdown hook.
   *
   * @param objects An array of objects to be closed on application shutdown.
   * @return The application shutdown hook {@link Thread}.
   */
  @VisibleForTesting
  static Thread buildShutdownHookThread(final Object... objects) {
    final Collection<AutoCloseable> autoCloseables = Stream.of(objects)
        .filter(o -> o != null)
        .filter(o -> o instanceof AutoCloseable)
        .map(o -> AutoCloseable.class.cast(o))
        .collect(Collectors.toList());

    return new Thread(() -> {
      autoCloseables.forEach(CloseableShutdownHook::close);
    });
  }

  private static void close(final AutoCloseable autoCloseable) {
    try {
      autoCloseable.close();
    } catch (final Exception e) {
      LOGGER.error("Unable to close object {}.", autoCloseable.getClass().getName(), e);
    }
  }

}
