summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/imagemaster/serverconnection/ImageProcessor.java
blob: 83062807f294eac24a770f283b601aa92667480a (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                 
                           
                          
                           
                         
                          

                            

                                              


                                       
                                                  
                                          
                                                          



                                                        



                                                                                 

                           
 
                                                                                   






                                                                                          
                          
                                     
           

                                                                                               



                                                                              





                                        
                                                                                               



                                                                                                      
           



                                                      
                                                                                                                      


                                           
 
                                                                                                               
 






                                             
 
                                                  








                                                                                                                                                                                                                                
                         





                                                                                
                                                                                               

                                                                                                                                 

                                                   

                                                                                                                                             
                 













                                                                                                                          
         
 



                                                                                                                          
                                                                                             












                                                                              
                                                                                                                




                                                                                                                   
                                                                                                   
 
                                                                           
                                                                          
                                                                                      



                                                       
                                                                           

                                                                                   
                                                                                                   
 
                                                                           
                                                                          
                                                                                      





                                                                               
                                                                                             







                                                                                      

                                                        
           

                                                                                      
                                     
           
                                                                                          
         


                                                         
 
                                                                 
 

                                                                           
                 
                

                              
 


                                                                                          
           

                      

                                                     
                                                

                                                     

                                     
                                                  
                                                               
                 
                                                               
                                                                                               
                                                                               
                                                                       
         

                                                             
         
                                                                 



                                                                         
 

                              
        


                                                                                  
              
         
                                                                  

                                                   


                                                                                                                         
                                                                 
                 
                                                                                                 
         



                                 
 
package org.openslx.imagemaster.serverconnection;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;
import org.openslx.imagemaster.Globals;
import org.openslx.imagemaster.crcchecker.CRCFile;
import org.openslx.imagemaster.db.DbImage;
import org.openslx.imagemaster.thrift.iface.DownloadInfos;
import org.openslx.imagemaster.thrift.iface.ImageData;
import org.openslx.imagemaster.thrift.iface.UploadInfos;
import org.openslx.imagemaster.util.RandomString;

/**
 * Processing the up- and download of images.
 * Handles who is authorized and knows which blocks are missing / need to be sent
 */
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: uploadingImageInfos
	 */
	private static Map<String, UploadingImage> uploadingImages = new ConcurrentHashMap<>();

	/**
	 * The UUIDs of the images that need to be checked by the crc checker.
	 */
	private static List<String> imagesToCheck = new LinkedList<>();

	/**
	 * The downloading clients.
	 * Key: serverSessionId
	 * Value: downloadingClientInfos
	 */
	private static HashMap<String, DownloadingClient> downloadingClients = 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, List<Integer> crcSums )
	{
		// check image data
		// TODO: do security checks

		log.debug( "Trying to answer request:" + serverSessionId + ", " + imageData + ", " + crcSums );

		String uuid = imageData.uuid;
		String token;
		String filepath;
		String crcPath;
		
		List<Integer> allBlocks;
		UploadingImage image;

		synchronized ( uploadingImages ) {
			// check if image is already uploading TODO: what if two clients call this at the same time? -> Good question.. (Thought about not sending the last requested. But then the upload will never finish...)
			if ( ( image = uploadingImages.get( uuid ) ) != null ) {
				log.debug( "Image is already uploading" );
				List<Integer> missing = getNMissingBlocks( image, AMOUNT );
				if ( missing.isEmpty() ) {
					uploadDone( uuid );
					return new UploadInfos( null, 0, missing );
				}
				return new UploadInfos( image.getToken(), Globals.getSslSocketPort(), missing );
			}

			// insert new image
			log.debug( "Inserting new download" );
			filepath = Globals.getImageDir() + "/" + uuid + ".vmdk";
			token = RandomString.generate( 100, false );
			crcPath = Globals.getImageDir() + "/" + uuid + ".crc";
			int nBlocks = (int)Math.ceil( imageData.fileSize / Globals.blockSize );
			allBlocks = new ArrayList<>( nBlocks );
			for ( int i = nBlocks - 1; i >= 0; i-- ) {			// fill empty list with all block numbers
				allBlocks.add( i );
			}
			image = new UploadingImage( token, allBlocks, new Timestamp( System.currentTimeMillis() ), uuid, filepath, crcPath );
			uploadingImages.put( uuid, image );
		}
		
   	try {
   		new CRCFile( crcSums, crcPath);
   	} catch (IOException e) {
   		log.debug( "Could not create crc file" );
   		return null;	// TODO: what to do if we can not write the crc file to disk? Give object to crcscheduler?
   	}

		ConnectionHandler.addConnection( token, filepath, Connection.UPLOADING ).image = image;
		DbImage.insert( imageData, System.currentTimeMillis(), token, allBlocks, serverSessionId, filepath );
		imagesToCheck.add( uuid );

		log.debug( "Returning UploadInfos" );
		return new UploadInfos( token, Globals.getSslSocketPort(), getNMissingBlocks( image, AMOUNT ) );
	}

	public static DownloadInfos getDownloadInfos( String serverSessionId, String uuid, List<Integer> requestedBlocks )
	{
		// check if server is already downloading
		if ( downloadingClients.containsKey( serverSessionId ) ) {
			DownloadingClient client = downloadingClients.get( serverSessionId );

			// remove download if done
			if ( requestedBlocks.isEmpty() )
			{
				downloadDone( serverSessionId, uuid );
				return new DownloadInfos();
			}

			if ( client.isDownloading( uuid ) )
			{
				// client was downloading this image
				// update the requested blocks
				client.requestBlocks( uuid, requestedBlocks );
				return new DownloadInfos( client.getToken( uuid ), Globals.getSslSocketPort() );
			}

			// server was downloading another image and now gets a new connection for this new download
			String token = RandomString.generate( 100, false );
			String filepath = DbImage.getImageByUUID( uuid ).imagePath;
			ConnectionHandler.addConnection( token, filepath, Connection.DOWNLOADING );

			client.addDownload( uuid, requestedBlocks, token );
			downloadingClients.put( serverSessionId, client );
			return new DownloadInfos( token, Globals.getSslSocketPort() );
		}

		// insert new client and start listener
		synchronized ( downloadingClients ) {
			DownloadingClient client = new DownloadingClient();
			String token = RandomString.generate( 100, false );
			String filepath = DbImage.getImageByUUID( uuid ).imagePath;
			ConnectionHandler.addConnection( token, filepath, Connection.DOWNLOADING );

			client.addDownload( uuid, requestedBlocks, token );
			downloadingClients.put( serverSessionId, client );
			return new DownloadInfos( token, Globals.getSslSocketPort() );
		}
	}

	private static void downloadDone( String serverSessionId, String uuid )
	{
		synchronized ( downloadingClients ) {
			DownloadingClient client = downloadingClients.get( serverSessionId );
			client.removeDownload( uuid );
			ConnectionHandler.removeConnection( client.getToken( uuid ) );
			if ( !client.hasDownloads() ) {
				downloadingClients.remove( serverSessionId );
			}
		}
	}

	/**
	 * 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> getNMissingBlocks( UploadingImage image, int amount )
	{
		int size = image.amountOfMissingBlocks();
		if ( amount > size )
			amount = size;

		List<Integer> result = new ArrayList<>( amount );

		for ( int i = 1; i <= amount; i++ ) {
			result.add( image.removeMissingBlock( size - 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 )
	{
		synchronized ( imagesToCheck ) {
			imagesToCheck.remove( uuid );
		}
		
		UploadingImage image;
		synchronized ( uploadingImages ) {
			image = uploadingImages.remove( uuid );
		}
		image.getDbImage().updateMissingBlocks( 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( image.getToken() );
	}

	public static List<UploadingImage> getImagesToCheck()
	{
		List<UploadingImage> result = new LinkedList<>();
		Iterator<String> iter = imagesToCheck.iterator();
		while ( iter.hasNext() ) {
			result.add( uploadingImages.get( iter.next() ) );
		}

		return result;
	}
	
	/**
	 * 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, Connection.UPLOADING );
			UploadingImage infos = new UploadingImage( token, image.missingBlocks, image.timestamp,
					image.uuid, image.imagePath, Globals.getImageDir() + "/" + image.uuid + ".crc" );
			uploadingImages.put( image.uuid, infos );
		}
		log.info( "Added " + list.size() + " pending upload(s) to process list again." );
	}

	public static void init()
	{
	}
}