/*
* Copyright 2014 Rackspace
*
* 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.
* Original author: gdusbabek
* Modified by: chinmay
*/
package com.rackspacecloud.blueflood.CloudFilesBackfiller.download;
import com.codahale.metrics.Timer;
import com.rackspacecloud.blueflood.utils.Metrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DownloadService {
private static final Logger log = LoggerFactory.getLogger(DownloadService.class);
private static final int MAX_FILES = 5;
private static final int MAX_UNEXPECTED_ERRORS = 5;
private final File downloadDir;
private final Thread thread;
private final Lock downloadLock = new ReentrantLock(true);
private FileManager fileManager = null; // todo: should be final.
private static Timer waitingTimer = Metrics.timer(DownloadService.class, "Delay introduced because cloud files were not consumed/merged");
private boolean running = false;
private boolean terminated = false;
private int unexpectedErrors = 0;
public DownloadService(final File downloadDir) {
this.downloadDir = downloadDir;
this.running = false;
this.thread = new Thread("Download Service") {
public void run() {
while (!terminated) {
// if there are > 1 tmp files in the dir, just wait.
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".json.gz.tmp");
}
};
while (downloadDir.listFiles(filter).length > 1) {
try {
sleep(200L);
} catch (InterruptedException ex) {
Thread.interrupted();
}
}
doCheck();
try {
sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
log.debug("Download service thread stopping");
}
};
this.thread.start();
}
public void setFileManager(FileManager newFileManager) {
downloadLock.lock();
try {
fileManager = newFileManager;
} finally {
downloadLock.unlock();
}
}
public synchronized void start() throws IOException {
if (terminated)
throw new IOException("Download service has been terminated. It cannot be restarted.");
if (running)
throw new IOException("Download service is already running.");
if (!downloadDir.exists())
throw new IOException("Download directory does not exist");
log.info("Resuming downloads");
running = true;
thread.interrupt();
}
public synchronized void stop() {
log.info("Stopping download service");
running = false;
thread.interrupt();
}
public synchronized void terminate(boolean waitFor) {
terminated = true;
stop();
log.info("Terminating download service");
thread.interrupt();
if (waitFor) {
log.info("Wating for download service termination");
while (thread.isInterrupted() || thread.isAlive()) {
try { Thread.sleep(100); } catch (Exception ex) {}
}
log.info("Download service terminated");
}
}
// gets run by the thread.
private void doCheck() {
if (!running) return;
if (fileManager == null) return;
if (unexpectedErrors > MAX_UNEXPECTED_ERRORS) {
log.info("Terminating because of errors");
terminate(false);
return;
}
Timer.Context waitTimerContext = waitingTimer.time();
// Possible infinite thread sleep? This will make sure we fire downloading only when are the files are consumed/merged
while (downloadDir.listFiles().length != 0) {
log.debug("Waiting for files in download directory to clear up. Sleeping for 1 min. If you see this persistently, it means the downloaded files are not getting merged properly/timely");
try { Thread.sleep(60000); } catch (Exception ex) {}
}
waitTimerContext.stop();
if (downloadLock.tryLock()) {
try {
if (fileManager.hasNewFiles()) {
fileManager.downloadNewFiles(downloadDir);
}
} catch (Throwable unexpected) {
unexpectedErrors += 1;
log.error("UNEXPECTED; WILL TRY TO RECOVER");
log.error(unexpected.getMessage(), unexpected);
// sleep for a minute?
if (Thread.interrupted()) {
try {
thread.sleep(60000);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
} finally {
downloadLock.unlock();
}
} else {
log.debug("Download in progress");
}
}
}