/*
 * Copyright © 2014 Cask Data, Inc.
 *
 * Licensed 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 co.cask.cdap.data.stream.service;

import co.cask.cdap.common.conf.CConfiguration;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.data.stream.StreamFileJanitor;
import com.google.common.util.concurrent.AbstractService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.twill.common.Threads;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Performs cleanup of stream files regularly with a single thread executor.
 */
@Singleton
public final class LocalStreamFileJanitorService extends AbstractService implements StreamFileJanitorService {

  private static final Logger LOG = LoggerFactory.getLogger(LocalStreamFileJanitorService.class);

  private final StreamFileJanitor janitor;
  private final long cleanupPeriod;
  private ScheduledExecutorService executor;

  @Inject
  public LocalStreamFileJanitorService(StreamFileJanitor janitor, CConfiguration cConf) {
    this.janitor = janitor;
    this.cleanupPeriod = cConf.getLong(Constants.Stream.FILE_CLEANUP_PERIOD);
  }

  @Override
  protected void doStart() {
    executor = new ScheduledThreadPoolExecutor(1, Threads.createDaemonThreadFactory("stream-cleanup")) {
      @Override
      protected void terminated() {
        notifyStopped();
      }
    };

    // Always run the cleanup when it starts
    executor.submit(new Runnable() {

      @Override
      public void run() {
        LOG.debug("Execute stream file cleanup.");

        try {
          janitor.cleanAll();
          LOG.debug("Completed stream file cleanup.");
        } catch (Throwable e) {
          LOG.error("Failed to cleanup stream file.", e);
        } finally {
          // Compute the next cleanup time. It is aligned to work clock based on the period.
          long now = System.currentTimeMillis();
          long delay = (now / cleanupPeriod + 1) * cleanupPeriod - now;

          if (delay <= 0) {
            executor.submit(this);
          } else {
            LOG.debug("Schedule stream file cleanup in {} ms", delay);
            executor.schedule(this, delay, TimeUnit.MILLISECONDS);
          }
        }
      }
    });
    notifyStarted();
  }

  @Override
  protected void doStop() {
    executor.shutdownNow();
  }
}

@@ -1028,33 +1028,24 @@
 current.
-Scheduled
 Executor
 Service;
@@ -1036,23 +1036,17 @@
 Executor
-Service
+s
 ;%0Aimport
@@ -1076,34 +1076,24 @@
 cheduled
-ThreadPool
 Executor
 ;%0Aimport
@@ -1084,16 +1084,23 @@
 Executor
+Service
 ;%0Aimport
@@ -1835,31 +1835,42 @@
 r = 
-new Schedu
+Executors.newSing
 le
-d
 Thread
-Pool
+Scheduled
 Exec
@@ -1878,11 +1878,8 @@
 tor(
-1, 
 Thre
@@ -1930,101 +1930,8 @@
 p%22))
- %7B%0A      @Override%0A      protected void terminated() %7B%0A        notifyStopped();%0A      %7D%0A    %7D
 ;%0A%0A 
@@ -2050,24 +2050,140 @@
 oid run() %7B%0A
+        if (state() != State.RUNNING) %7B%0A          LOG.info(%22Janitor already stopped%22);%0A          return;%0A        %7D%0A%0A
         LOG.
@@ -2966,24 +2966,194 @@
  doStop() %7B%0A
+    executor.submit(new Runnable() %7B%0A      @Override%0A      public void run() %7B%0A        LOG.debug(%22Stream file janitor stopped%22);%0A        notifyStopped();%0A      %7D%0A    %7D);%0A
     executor
@@ -3165,11 +3165,8 @@
 down
-Now
 ();%0A

Fix a race condition issue about using Executor.terminate. See https://issues.apache.org/jira/browse/TWILL-110