summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java
blob: 5b2a931bd746d2760ee280f69aa77197688fbf31 (plain) (tree)
1
2
3
4
5
6
7
8
9





                                     
                           
                                              
                      
                     




                                    
                                                    

                                                      
                                                       

                                                        

                                                             

                                                         
                                                          
                                                    
                                                  




                                                  
                                                   
                                                     


                                              
                             




                                                                                   
                                                                                                
           
                                                             











                                                                                          
                       




































                                                                                                                          



















                                                                                                                                                                                                                    




                                                                                              















































                                                                                                                                              

















                                                                                                           

                                                                                                         





                                                                                                                












                                                                                                                    
                                                                                                                   














                                                                                                                                                
                                                                                                             

                 




















































                                                                                                               
                                              











                                                                                                           
                                                  







                                                                                                                       
















                                                                                                                                          

         
           

                              




                                     
                                                                
                                                                                     

                                                                                                    
                                                  

                                                     
                                                                                         


                                                                      
                                                                                                               
         








































































                                                                                                                                                   

                                                                                                
           
                                                                
          
                                           

                                                                                          













                                                                               



                                                                                                





                                                                                                            
                                                                                                
                                                                                     







                                                                                                                                   
                               

                                       




                                                    








                                                                                                                                             

                                                        
                                                                                                             













                                                                                    














































                                                                                                                                                  









                                                                                                    

 
 
package org.openslx.dozmod.thrift;

import java.awt.Frame;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;

import javax.swing.JFileChooser;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.openslx.bwlp.thrift.iface.ImageBaseWrite;
import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
import org.openslx.bwlp.thrift.iface.ImagePermissions;
import org.openslx.bwlp.thrift.iface.ImageVersionWrite;
import org.openslx.bwlp.thrift.iface.LecturePermissions;
import org.openslx.bwlp.thrift.iface.LectureWrite;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.bwlp.thrift.iface.TImageDataException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.filetransfer.AsyncHashGenerator;
import org.openslx.dozmod.filetransfer.DownloadTask;
import org.openslx.dozmod.filetransfer.UploadTask;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.Gui.GuiCallable;
import org.openslx.dozmod.gui.MainWindow;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.gui.helper.QFileChooser;
import org.openslx.dozmod.gui.wizard.LectureWizard;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import org.openslx.util.Util;

public class ThriftActions {

	private static final Logger LOGGER = Logger.getLogger(ThriftActions.class);

	/* *******************************************************************************	
	 * 
	 *                             IMAGE VERSION DOWNLOAD
	 *
	 * Download image version action composed of the interface 'DownloadCallback'
	 * and the actual static method 'initDownload' to start the download.
	 * 
	 ********************************************************************************/
	/**
	 * The callback interface to inform the GUI about the status of the operation 
	 */
	public interface DownloadCallback {
		void downloadInitialized(boolean success);
	}
	/**
	 * NON-BLOCKING
	 * Initializes the download of the given imageVersionId saving it to the given
	 * imageName
	 * 
	 * @param frame caller of this method
	 * @param imageVersionId image version id to download
	 * @param imageName destination file name
	 * @param virtualizerId id of the virtualizer
	 * @param imageSize size in bytes of the image to be downloaded
	 * @param callback callback function to return status of this operation to the GUI 
	 */
	public static void initDownload(final Frame frame, final String imageVersionId, final String imageName,
			final String virtualizerId, final long imageSize, final DownloadCallback callback) {
		QFileChooser fc = new QFileChooser(Config.getDownloadPath(), true);
		fc.setDialogTitle("Bitte wählen Sie einen Speicherort");
		int action = fc.showSaveDialog(frame);
		final File file = fc.getSelectedFile();
		if (action != JFileChooser.APPROVE_OPTION || file == null)
			return;

		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				final TransferInformation transInf;
				try {
					transInf = ThriftManager.getSatClient()
							.requestDownload(Session.getSatelliteToken(), imageVersionId);
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Die Download-Anfrage ist gescheitert");
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}

				File df = null;
				try {
					file.getAbsoluteFile().mkdirs();
					df = new File(file.getAbsolutePath(), generateFilename(imageName, virtualizerId));
					final File ff = df;
					if (df.exists()) {
						if (!Gui.syncExec(new GuiCallable<Boolean>() {
							@Override
							public Boolean run() {
								return Gui.showMessageBox(frame,
										"Datei '" + ff.getAbsolutePath() + "' existiert bereits, wollen Sie sie überschreiben?", MessageType.QUESTION_YESNO, LOGGER, null);
							}
						})) {
							// user aborted
							return;
						} else {
							// delete it
							if (!df.delete()) {
								// TODO what?
								Gui.showMessageBox(frame, "Datei konnte nicht überschrieben werden!", MessageType.ERROR, LOGGER, null);
								return;
							}
						}
					}
					df.createNewFile();
				} catch (IOException e) {
					LOGGER.warn("Cannot prepare download destination", e);
				}
				final File destFile = df;
				final DownloadTask dlTask;
				try {
					dlTask = new DownloadTask(Session.getSatelliteAddress(), transInf.getPlainPort(), transInf
							.getToken(), destFile, imageSize, null);
				} catch (final FileNotFoundException e) {
					Gui.asyncMessageBox("Konnte Download nicht vorbereiten: Der gewählte Zielort ist nicht beschreibbar",
							MessageType.ERROR, LOGGER, e);
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}
				new Thread(dlTask).start();

				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						Config.setDownloadPath(file.getAbsolutePath());
						MainWindow.addDownload(imageName, destFile.getName(), dlTask);
						if (callback != null)
							callback.downloadInitialized(true);
					}
				});
			}
		});
	}
	/**
	 * Generates a filename based on the given imageName and with the proper extension
	 * depending on the virtualizer
	 * 
	 * @param imageName
	 * @param virtualizerId
	 * @return the generated name as String
	 */
	private static String generateFilename(String imageName, String virtualizerId) {
		String fileName = imageName.replaceAll("[^a-zA-Z0-9_\\.\\-]+", "_");
		if (fileName.length() > 50) {
			fileName = fileName.substring(0, 50);
		}
		if ("vmware".equals(virtualizerId)) {
			fileName += ".vmdk";
		} else if ("virtualbox".equals(virtualizerId)) {
			fileName += ".vdi";
		} else {
			fileName += ".img";
		}
		return fileName;
	}

	/* *******************************************************************************	
	 * 
	 *                              IMAGE CREATION
	 *
	 * Creates a base image with the given name
	 * 
	 ********************************************************************************/
	/**
	 * GUI-BLOCKING
	 * Creates the image with the given name. Returns the uuid returned by the server
	 * 
	 * @param frame calling this action
	 * @return uuid as String, or null if the creation failed
	 */
	public static String createImage(final Frame frame, final String name) {
		String uuid = null;
		try {
			uuid = ThriftManager.getSatClient().createImage(Session.getSatelliteToken(), name);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Erstellen des Images fehlgeschlagen");
		} catch (Exception e) {
			Gui.showMessageBox(frame, "Unbekannter Fehler beim Erstellen der VM", MessageType.ERROR,
					LOGGER, e);
		}
		return uuid;
	}
	/**
	 * GUI-BLOCKING
	 * Pushes a new image base to the server with the given imageBaseId and the meta information in meta
	 * 
	 * @param frame to show user feedback on
	 * @param imageBaseId image's id we are writing meta information of
	 * @param meta actual meta information as ImageBaseWrite
	 */
	public static void updateImageBase(final Frame frame, final String imageBaseId, final ImageBaseWrite meta) {
		try {
			ThriftManager.getSatClient().updateImageBase(Session.getSatelliteToken(),
					imageBaseId, meta);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Metadaten des Images nicht übertragen");
		}
	}
	/**
	 * GUI-BLOCKING
	 * Pushes the given permission map as custom permission for the given imageBaseId
	 * 
	 * @param frame to show user feedback on
	 * @param imageBaseId image's id we are writing permissions of
	 * @param permissions actual permissions map to write
	 */
	public static void writeImagePermissions(final Frame frame, final String imageBaseId, final Map<String, ImagePermissions> permissions) {
		try {
			ThriftManager.getSatClient().writeImagePermissions(Session.getSatelliteToken(),
					imageBaseId, permissions);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Berechtigungen nicht übertragen");
		}
	}

	/* *******************************************************************************	
	 * 
	 *                              IMAGE VERSION UPLOAD
	 *
	 * Methods to upload an image version. This is compromised of two distinct steps:
	 *  - requestVersionUpload(..) to request the upload at the server
	 *  - initUpload(..) to actually start the upload of the file
	 * 
	 ********************************************************************************/
	/**
	 * GUI-BLOCKING
	 * Request the upload of an image version. Returns the TransferInformation received by the server
	 * or null if the request failed. Will give user feedback about failures.
	 * 
	 * @param frame caller of this method
	 * @param imageBaseId uuid of the image to upload a version of
	 * @param fileSize size in bytes(?) of the uploaded file
	 * @param blockHashes
	 * @param machineDescription
	 * @param callback
	 * @return TransferInformation received by the server, null if the request failed.
	 */
	public static TransferInformation requestVersionUpload(final Frame frame, final String imageBaseId,
			final long fileSize, final List<ByteBuffer> blockHashes,
			final ByteBuffer machineDescription) {
		TransferInformation ti = null;
		try {
			ti = ThriftManager.getSatClient().requestImageVersionUpload(
					Session.getSatelliteToken(),
					imageBaseId,
					fileSize,
					null, // TODO remove deprecated parameter
					machineDescription);
			LOGGER.info("Version upload granted, versionId: '" + ti.toString());
		} catch (TAuthorizationException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Upload einer neuen Version nicht erlaubt!");
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Upload-Anfrage gescheitert!");
		}
		return ti;
	}
	/**
	 * GUI-BLOCKING
	 * Starts uploading the given diskFile using the transferInformation and hashGen
	 *  
	 * @param frame caller of this method
	 * @param transferInformation transfer information to use for the upload
	 * @param hashGen hash generator for this file
	 * @param diskFile the file to upload
	 * @return UploadTask if the uploading initialized, or null if uploading failed
	 */
	public static UploadTask initUpload(final Frame frame, final TransferInformation transferInformation,
			final File diskFile) {
		UploadTask uploadTask = null;
		// do actually start the upload now
		LOGGER.debug("Starting upload for: " + diskFile.toPath());
		try {
			uploadTask = new UploadTask(Session.getSatelliteAddress(),
					transferInformation.getPlainPort(), transferInformation.getToken(),
					diskFile);
		} catch (FileNotFoundException e) {
			Gui.asyncMessageBox("Kann VM nicht hochladen: Datei nicht gefunden\n\n"
					+ diskFile.getAbsolutePath(), MessageType.ERROR, LOGGER, e);
			return null;
		}
		AsyncHashGenerator hashGen = null;
		try {
			hashGen = new AsyncHashGenerator(transferInformation.token, diskFile);
			hashGen.start();
			Util.sleep(50); // A little ugly... Give the hash generator a head start
		} catch (FileNotFoundException | NoSuchAlgorithmException e) {
			Gui.showMessageBox(frame, "Kann keine Block-Hashes für den Upload berechnen, "
					+ "automatische Fehlerkorrektur deaktiviert.", MessageType.WARNING, LOGGER, e);
		}
		Util.sleep(50); // A little ugly... Give the hash generator a head start
		Thread uploadThread = new Thread(uploadTask);
		uploadThread.setDaemon(true);
		uploadThread.start();
		do { // Even more ugly - block the GUI thread so we know whether the upload started, and only then switch to the next page
			Util.sleep(5);
		} while (uploadTask.getFailCount() == 0 && uploadTask.getTransferCount() == 0
				&& !uploadTask.isCanceled());

		if (uploadTask.getTransferCount() == 0) {
			Gui.asyncMessageBox("Aufbau der Verbindung zum Hochladen fehlgeschlagen", MessageType.ERROR,
					LOGGER, null);
			hashGen.cancel();
			uploadTask.cancel();
			uploadTask = null;
		}
		return uploadTask;
	}
	
	/**
	 * GUI-BLOCKING
	 * Gives user feedback
	 * TODO
	 * @param frame
	 * @param transferInformation
	 * @param versionInfo
	 */
	public static void updateImageVersion(final Frame frame,
			final String versionId, final ImageVersionWrite versionInfo){
		try  {
			ThriftManager.getSatClient().updateImageVersion(Session.getSatelliteToken(),
					versionId,
					versionInfo);
		} catch (TException e) {
			Gui.showMessageBox(frame, "Konnte neue Version nicht erstellen!",
					MessageType.ERROR, LOGGER, e);
			return;
		}
		Gui.showMessageBox(frame, "Neue Version erfolgreich erstellt", MessageType.INFO, LOGGER, null);
	}

	/* *******************************************************************************	
	 * 
	 *                              IMAGE METADATA QUERY
	 *
	 * Fetches image details or permissions
	 * 
	 ********************************************************************************/
	public interface MetadataCallback {
		void fetchedImageDetails(ImageDetailsRead details);
		void fetchedImagePermissions(Map<String, ImagePermissions> permissions);
//		void fetchedLectureDetails(LectureRead details);
//		void fetchedLecturePermissions(Map<String, ImagePermissions> permissions);
	}
	
	public static void getImageDetails(final Frame frame, final String imageBaseId, final MetadataCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			ImageDetailsRead details = null;
			@Override
			public void fire() {
				try {
					details = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(),
							imageBaseId);
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Lesen der Metadaten");
					if (callback != null)
						callback.fetchedImageDetails(details);
					return;
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedImageDetails(details);
						}
					}
				});
			}
		});
	}
	/**
	 * @param frame
	 * @param imageBaseId
	 * @param callback
	 */
	public static void getImagePermissions(final Frame frame, final String imageBaseId, final MetadataCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			Map<String, ImagePermissions> permissionMap = null;
			@Override
			public void fire() {
				try {
					permissionMap = ThriftManager.getSatClient().getImagePermissions(Session.getSatelliteToken(), imageBaseId);
					LOGGER.debug("Received: " + permissionMap);
					if (permissionMap == null)
						LOGGER.debug("And NULL");
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Lesen der Metadaten");
					if (callback != null)
						callback.fetchedImagePermissions(null);
					return;
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedImagePermissions(permissionMap);
						}
					}
				});
			}
		});
	}

	
	/* *******************************************************************************	
	 * 
	 *                              IMAGE / VERSION DELETION
	 *
	 * Deletes a specific image version
	 * 
	 ********************************************************************************/
	/**
	 * Delete callback interface to be implemented by callers of 
	 * ThriftActions.deleteImageVersion(..)
	 */
	public interface DeleteCallback {
		/**
		 * Called once the status of a delete operation is determined
		 * 
		 * @param success true if deleted successfully, false otherwise
		 */
		void isDeleted(boolean success);
	}
	/**
	 * NON-BLOCKING
	 * Deletes either an image base or an image version depending on the parameters. 
	 * To delete an image base, give the imageBaseId and let imageVersionId be null.
	 * To delete an image version, set both imageBaseId and imageVersionId.
	 * The success of the operation will be forwarded to the GUI through the DeleteCallback.
	 * 
	 * @param frame next parent frame of the caller of this method
	 * @param imageBaseId uuid of the image that belongs to the version
	 * @param imageVersionId id of the image version to be deleted
	 * @param callback called to inform the GUI about the deletion status (see DeleteCallback interface)
	 */
	public static void deleteImageBaseOrVersion(final Frame frame, final String imageBaseId,
			final String imageVersionId, final DeleteCallback callback) {
		String questionText;
		if (imageBaseId == null) {
			return;
		} else {
			questionText = imageVersionId == null ?
				"Wollen Sie dieses Image wirklich löschen?" : "Wollen Sie diese Image-Version wirklich löschen?";
		}
		if (!userConfirmed(frame, questionText))
			return;

		// perform the deletion
		QuickTimer.scheduleOnce(new Task() {
			boolean success = false;
			@Override
			public void fire() {
				try {
					if (imageVersionId == null) {
						// deleting an image base
						ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), imageBaseId);
						LOGGER.info("Deleted image with id '" + imageBaseId + "'");
					} else {
						// deleting an image version
						ThriftManager.getSatClient().deleteImageVersion(Session.getSatelliteToken(), imageVersionId);
						LOGGER.info("Deleted version '" + imageVersionId + "' of image '" + imageBaseId + "'.");
					}
					success = true;
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Löschen fehlgeschlagen");
					if (callback != null)
						callback.isDeleted(success);
					return;
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null)
							callback.isDeleted(success);
					}
				});
			}
		});
	}

	/* *******************************************************************************	
	 * 
	 *                              LECTURE CREATION
	 *
	 * Methods to create lectures 
	 * 
	 ********************************************************************************/
	/**
	 * Creates a lecture with the given meta data
	 * 
	 * @param frame to show user feedback on
	 * @param meta actual meta data as LectureWrite
	 * @return the created lecture's id if it worked, null otherwise
	 */
	public static String createLecture(final Frame frame, final LectureWrite meta) {
		if (meta == null)
			return null;
		String uuid = null;
		try {
			// push to sat
			uuid = ThriftManager.getSatClient().createLecture(Session.getSatelliteToken(), meta);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Failed to create lecture");
		}
		return uuid;
	}
	/**
	 * Writes custom lecture permissions (permissions param) for the given lectureId.
	 * 
	 * @param frame to show user feedback on
	 * @param lectureId lecture's id to write custom permissions for
	 * @param permissions actual permission map to push
	 */
	public static void writeLecturePermissions(final Frame frame, final String lectureId, final Map<String, LecturePermissions> permissions) {
		try {
				ThriftManager.getSatClient().writeLecturePermissions(Session.getSatelliteToken(), lectureId, permissions);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Failed to write lecture permissions");
		}
	}

	/* *******************************************************************************	
	 * 
	 *                              PRIVATE HELPERS 
	 *
	 ********************************************************************************/
	/** 
	 * Helper to ask the user for confirmation. Returns his choice.
	 * 
	 * @param frame frame to show this message box on
	 * @param message question message to display to the user
	 * @return true if the user confirmed (clicked yes), false otherwise
	 */
	private static boolean userConfirmed(final Frame frame, final String message) {
		return Gui.showMessageBox(frame, message, MessageType.QUESTION_YESNO, LOGGER, null);
	}


}