content: 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.Map;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import org.apache.commons.exec.StreamPumper;
  import org.openqa.grid.internal.ExternalSessionKey;
  
  import com.google.common.cache.Cache;
  import com.google.common.cache.CacheBuilder;
  import com.google.common.cache.RemovalListener;
  import com.google.common.cache.RemovalNotification;
  
  /**
   * A plugin to temporarily store videos in temporary local files. Videos are deleted when
   * we store over a certain number of them (currently 200) to save on disk space. Use this plugin if:
   * 
   * <ul>
   * <li>You are just getting your grid setup, and you don't want the added complexity</li>
   * <li>You don't mind the grid forgetting about videos over restarts</li>
   * <li>You don't need the grid to store videos to be stored for very long</li>
   * </ul>
   * @author Alasdair Macmillan
   *
   */
  public class LocalTempFileStore implements IVideoStore {
  	
  	private static final Logger log = Logger.getLogger(LocalTempFileStore.class.getName());
  
  	
  	private static Cache<ExternalSessionKey, File> availableVideos;
  	
  	static {
  		availableVideos = CacheBuilder.newBuilder().maximumSize(200).removalListener(new RemovalListener<ExternalSessionKey, File>() {
  			@Override
  			public void onRemoval(RemovalNotification<ExternalSessionKey, File> arg0) {
  				if(arg0.getValue().delete()) {
  					log.info("Deleted recording due to excess videos: " + arg0.getKey());
  				}
  			}
  		}).build();
  	}
  	
  	@Override
  	public void storeVideo(InputStream videoStream, long contentLength, String mimeType,
  			String sessionId,  SessionInfoBean infoBean) throws Exception {
  		File outputFile = File.createTempFile("screencast", ".webm");
  		FileOutputStream outputStream = new FileOutputStream(outputFile);
  		try {
  			new StreamPumper(videoStream, outputStream).run();
  		}
  		finally {
  			outputStream.close();
  		}
  		
  		availableVideos.put(new ExternalSessionKey(sessionId), outputFile);
  		log.info("Successfully retrieved video for session: " + sessionId + " and temporarily stashed it at: " + outputFile);
  	
  	}
  
  	@Override
  	public LocalTempFileDownloadContext retrieveVideo(String sessionId) throws Exception {
  		File file = availableVideos.getIfPresent(new ExternalSessionKey(sessionId));
  		if(file != null && file.exists() && file.isFile()) {
  			return new LocalTempFileDownloadContext(file);
  		}
  		
  		return new LocalTempFileDownloadContext(null);
  	}
  	
  	@Override
  	public StoredVideoInfoContext getVideoInformation(String sessionId)
  			throws Exception {
  		// XXX: Abuse the class heirarchy for this one
  		return retrieveVideo(sessionId);
  	}
  	
  	@Override
  	public String getVideoStoreTypeIdentifier() {
  		return "TEMP_FILE";
  	}
  	
  	private static class LocalTempFileDownloadContext implements StoredVideoDownloadContext, StoredVideoInfoContext {
  		
  		private File file;
  		private FileInputStream stream;
  
  		public LocalTempFileDownloadContext(File file) throws FileNotFoundException {
  			this.file = file;
  			if(file != null) {
  				stream = new FileInputStream(file);	
  			}
  			else {
  				stream = 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 Collections.emptyMap();
  		}
  	}
  
  }
  