package org.openslx.imagemaster.serverconnection;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.openslx.imagemaster.Globals;
import org.openslx.imagemaster.db.DbImage;
import org.openslx.imagemaster.thrift.iface.ImageData;
import org.openslx.imagemaster.thrift.iface.UploadInfos;
import org.openslx.imagemaster.util.RandomString;
public class ImageProcessor
{
private static final Logger log = Logger.getLogger( ImageProcessor.class );
/**
* The amount of blocks that is return in UploadInfos (after request of satellite)
*/
private static final int AMOUNT = 20;
/**
* The uploading images.
* Key: imageUUID,
* Value: imageInfos
*/
private static HashMap<String, ImageInfos> uploadingImages = new HashMap<>();
/**
* Checks if this image is already uploading and returns a new list with missing blocks if so.
* Puts the new image into processing list else.
*
* @param serverSessionId The uploading server
* @param imageData The data of the image
* @return
*/
public static UploadInfos getUploadInfos( String serverSessionId, ImageData imageData )
{
// check image data
// TODO: do security checks
String uuid = imageData.uuid;
// check if image is already uploading TODO: what if two clients call this at the same time?
if ( uploadingImages.containsKey( uuid ) ) {
List<Integer> missing = getMissingBlocks( uuid, AMOUNT );
if ( missing.isEmpty() ) {
String token = uploadingImages.get( uuid ).getToken();
uploadDone( uuid );
return new UploadInfos( token, missing );
}
uploadingImages.get( uuid ).setLastSentBlocks( missing );
return new UploadInfos( uploadingImages.get( uuid ).getToken(), missing );
}
// insert new image and generate token
synchronized ( uploadingImages ) {
int nBlocks = (int)Math.ceil( imageData.fileSize / Globals.blockSize );
List<Integer> allBlocks = new LinkedList<>();
for ( int i = 0; i < nBlocks; i++ ) { // fill empty list with all block numbers
allBlocks.add( i );
}
String token = RandomString.generate( 100, false );
String filepath = Globals.getImageDir() + "/" + uuid + ".vmdk";
ConnectionHandler.addConnection( token, filepath );
// TODO: proper synchronization, interface is multi threaded.
// should synchronize operations on the map (use concurrent map) and then synchronize on the uploading image
// when handing the missing blocks etc...
uploadingImages.put( uuid, new ImageInfos( token, allBlocks, serverSessionId, new Timestamp( System.currentTimeMillis() ) ) );
DbImage.insert( imageData, System.currentTimeMillis(), token, allBlocks, serverSessionId, filepath );
List<Integer> missing = getMissingBlocks( uuid, AMOUNT );
if ( missing.isEmpty() ) {
// TODO: if this is empty, check if there are pending blocks and if so, request them again
uploadDone( uuid );
}
uploadingImages.get( uuid ).setLastSentBlocks( missing );
return new UploadInfos( token, missing );
}
}
/**
* Returns a specified number of missing blocks.
*
* @param imageUUID The image of which you want to get the missing blocks from
* @param amount The amount of blocks that you want to get
* @return The missing blocks
*/
private static List<Integer> getMissingBlocks( String imageUUID, int amount )
{
List<Integer> list = uploadingImages.get( imageUUID ).getMissingBlocks();
List<Integer> result = new LinkedList<>();
if ( amount > list.size() )
amount = list.size();
for ( int i = 0; i < amount; i++ ) {
result.add( list.get( i ) );
}
return result;
}
/**
* Is triggered when an upload of an image is done.
* Removes image from process list, updates db entry and moves file on hard drive.
*
* @param uuid
*/
private static void uploadDone( String uuid )
{
String token;
synchronized ( uploadingImages ) {
token = uploadingImages.remove( uuid ).getToken();
}
DbImage.updateMissingBlocks( uuid, null );
// file was already downloaded in the right location by the updownloader class.
// remove the connection so that it can be used by a new client
ConnectionHandler.removeConnection( token );
}
/**
* Checks pending uploads in database and adds them to process list again.
*/
static
{
List<DbImage> list = DbImage.getUploadingImages();
for ( DbImage image : list ) {
String token = image.token;
ConnectionHandler.addConnection( token, image.imagePath );
ImageInfos infos = new ImageInfos( token, image.missingBlocks, image.serverSessionId, image.timestamp );
uploadingImages.put( image.UUID, infos );
}
log.info( "Added " + list.size() + " pending upload(s) to process list again." );
}
}