package com.aimmac23.hub.videostorage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.exec.StreamPumper;
/**
* A plugin to store permamently videos in a local filesystem directory.
*
* This should be used when:
* <ul>
* <li>You don't want your Selenium Grid to delete videos</li>
* <li>You don't want your Selenium Grid to forget about videos over restarts</li>
* <li>You are trying to integrate with something that needs to access videos directly
* (NOT through the Hub), and you so happen to share a filesystem with that thing. </li>
* </ul>
*
* @author Alasdair Macmillan
*
*/
public class LocalFileVideoStore implements IVideoStore {
private static final Logger log = Logger.getLogger(LocalFileVideoStore.class.getName());
private File directory;
public LocalFileVideoStore() {
String path = System.getProperty("video.path");
if(path == null) {
throw new IllegalArgumentException("'video.path' is not defined - "
+ "you need to pass -Dvideo.path=<directory> to use " + this.getClass().getName());
}
directory = new File(path);
if(!directory.exists()) {
directory.mkdirs();
}
if(!directory.exists()) {
throw new IllegalStateException("Target directory does not exist: " + directory);
}
if(!directory.isDirectory()) {
throw new IllegalStateException("Target directory is not a directory: " + directory);
}
if(!directory.canWrite()) {
throw new IllegalStateException("Target directory is now writeable: " + directory);
}
}
@Override
public void storeVideo(InputStream videoStream, long contentLength, String mimeType,
String sessionId, SessionInfoBean infoBean) throws Exception {
File target = new File(directory, sessionId + ".webm");
FileOutputStream fileStream = new FileOutputStream(target);
try {
new StreamPumper(videoStream, fileStream).run();
}
finally {
fileStream.close();
}
log.info("Successfully written video file for session to: " + target);
}
@Override
public LocalFileVideoStoreDownloadContext retrieveVideo(String sessionId)
throws Exception {
File target = new File(directory, sessionId + ".webm");
if(target.exists() && target.isFile() && target.canRead()) {
return new LocalFileVideoStoreDownloadContext(target);
}
log.info("File not found, or is not readable for sessionId: " + sessionId);
return new LocalFileVideoStoreDownloadContext(null);
}
@Override
public StoredVideoInfoContext getVideoInformation(String sessionId)
throws Exception {
return retrieveVideo(sessionId);
}
@Override
public String getVideoStoreTypeIdentifier() {
return "LOCAL_FILE";
}
private static class LocalFileVideoStoreDownloadContext implements StoredVideoDownloadContext, StoredVideoInfoContext {
private File file;
private FileInputStream stream;
private String canonicalPath;
public LocalFileVideoStoreDownloadContext(File file) throws FileNotFoundException, IOException {
this.file = file;
if(file != null && file.exists()) {
stream = new FileInputStream(file);
this.canonicalPath = file.getCanonicalPath();
}
else {
stream = null;
canonicalPath = null;
}
}
@Override
public boolean isVideoFound() {
return file != null;
}
@Override
public InputStream getStream() throws IOException {
return stream;
}
@Override
public Long getContentLengthIfKnown() {
return new Long(file.length());
}
@Override
public void close() {
if(stream == null) {
// nothing to do
return;
}
try {
stream.close();
} catch (IOException e) {
log.log(Level.WARNING, "Could not close file: " + file, e);
}
}
@Override
public Map<String, Object> additionalInformation() {
return new HashMap<String, Object>(Collections.singletonMap("path", canonicalPath));
}
}
}