summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/imagemaster/serverconnection/ImageProcessor.java
blob: 71ef97be648161f8f2be6afa4b4a924f4153d556 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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." );
	}
}