package com.rayo.server.recording;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.media.mscontrol.Value;
import javax.media.mscontrol.mediagroup.FileFormatConstants;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.math.RandomUtils;
import com.rayo.core.verb.Output;
import com.rayo.core.verb.Record;
import com.voxeo.logging.Loggerf;
/**
* <p>A local temporary Store to store recordings. This implementation will
* store the recordings in the form /BASE_FOLDER/day/hour/second/rayo12345.wav</p>
*
* <p>This store is not persistent. Recordings will be self-deleted after the
* number of seconds specified in the deleteAfter attribute.</p>
*
* @author martin
*
*/
public class LocalTemporaryStore implements LocalStore {
private final static Loggerf log = Loggerf.getLogger(LocalTemporaryStore.class);
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd/hh/mm/");
private long deleteAfter = 60*60*24*1000; // By default, delete files after one day
private String baseFolder = "/tmp/recordings"; // By deafult store recordings in /tmp/recordings
private long cleanupInterval = 60*60*24; // Interval to check if files should be deleted. By default wait 1 day.
private int maxAttempts = 20; // Max attempts to get a valid file name
private ScheduledExecutorService scheduledExecutor;
private ScheduledFuture<?> future;
private Lock parentFolder = new ReentrantLock();
public void init() {
if (future != null) {
future.cancel(true);
}
future = scheduledExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
cleanup();
}
}, cleanupInterval, cleanupInterval, TimeUnit.SECONDS);
}
private void cleanup() {
log.info("Cleaning up task started.");
File root = new File(baseFolder);
checkToClean(root, System.currentTimeMillis());
}
private void checkToClean(File file, long now) {
log.trace("Checking to clean: " + file.getAbsolutePath());
if (file.isFile()) {
if (now - file.lastModified() > deleteAfter) {
log.trace("Deleting: " + file.getAbsolutePath());
try {
FileUtils.forceDelete(file);
} catch (IOException e) {
log.error("Could not delete file %s.", file.getAbsolutePath());
}
}
} else {
for(File child: file.listFiles()) {
checkToClean(child, now);
}
}
}
@Override
public File createRecording(Record recording) throws IOException {
File f = null;
int i = 0;
do {
f = new File(getTentativePath(recording));
} while(f.exists() && i < maxAttempts);
if (i > maxAttempts) {
throw new IOException("Could not create file at " +
baseFolder + ". Max attempts (" + maxAttempts +
") to create recording exceeded.");
}
if (!f.getParentFile().exists()) {
parentFolder.lock();
try {
if (!f.getParentFile().mkdirs()) {
throw new IOException("Could not create directory structure for " + f.getAbsolutePath());
}
} finally {
parentFolder.unlock();
}
}
if (!f.createNewFile()) {
throw new IOException("Could not create file " + f.getAbsolutePath());
}
return f;
}
private String getTentativePath(Record recording) {
String path = baseFolder;
if (!baseFolder.endsWith("/")) path+="/";
return path + formatter.format(new Date()) + "rayo" +
RandomUtils.nextInt(900000)+getExtensionFromFormat(recording);
}
private String getExtensionFromFormat(Record model) {
if (model.getFormat() != null) {
Value format = Output.toFileFormat(model.getFormat());
if (format.equals(FileFormatConstants.FORMAT_3G2)) {
return ".3g2";
} else if (format.equals(FileFormatConstants.FORMAT_3GP)) {
return ".3gp";
} else if (format.equals(FileFormatConstants.GSM)) {
return ".gsm";
} else if (format.equals(FileFormatConstants.INFERRED)) {
return ".mp3";
} else if (format.equals(FileFormatConstants.RAW)) {
return ".raw";
} else if (format.equals(FileFormatConstants.WAV)) {
return ".wav";
}
}
return ".wav";
}
public long getDeleteAfter() {
return deleteAfter;
}
public void setDeleteAfter(long deleteAfter) {
this.deleteAfter = deleteAfter * 1000;
}
public String getBaseFolder() {
return baseFolder;
}
public void setBaseFolder(String baseFolder) {
this.baseFolder = baseFolder;
}
public void setScheduledExecutor(ScheduledExecutorService scheduledExecutor) {
this.scheduledExecutor = scheduledExecutor;
}
public long getCleanupInterval() {
return cleanupInterval;
}
public void setCleanupInterval(long cleanupInterval) {
this.cleanupInterval = cleanupInterval;
}
}