package org.openslx.dozmod.thrift;
import java.awt.Frame;
import java.awt.Window;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.JFileChooser;
import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;
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.ImageVersionDetails;
import org.openslx.bwlp.thrift.iface.ImageVersionWrite;
import org.openslx.bwlp.thrift.iface.LecturePermissions;
import org.openslx.bwlp.thrift.iface.LectureRead;
import org.openslx.bwlp.thrift.iface.LectureSummary;
import org.openslx.bwlp.thrift.iface.LectureWrite;
import org.openslx.bwlp.thrift.iface.Satellite;
import org.openslx.bwlp.thrift.iface.SatelliteServer.Client;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.bwlp.thrift.iface.TInvocationException;
import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.bwlp.thrift.iface.TransferState;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.bwlp.thrift.iface.WhoamiInfo;
import org.openslx.dozmod.App;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.Config.SavedSession;
import org.openslx.dozmod.authentication.Authenticator.AuthenticationData;
import org.openslx.dozmod.filetransfer.DownloadTask;
import org.openslx.dozmod.filetransfer.TransferEvent;
import org.openslx.dozmod.filetransfer.TransferEventListener;
import org.openslx.dozmod.gui.GraphicalCertHandler;
import org.openslx.dozmod.gui.Gui;
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.window.SatelliteListWindow;
import org.openslx.dozmod.thrift.cache.ImageCache;
import org.openslx.dozmod.thrift.cache.LectureCache;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.dozmod.thrift.cache.UserCache;
import org.openslx.dozmod.util.FormatHelper;
import org.openslx.dozmod.util.VmWrapper;
import org.openslx.dozmod.util.VmWrapper.MetaDataMissingException;
import org.openslx.sat.thrift.version.Version;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import org.openslx.util.vm.DiskImage;
import org.openslx.util.vm.DiskImage.UnknownImageFormatException;
public class ThriftActions {
private static final Logger LOGGER = Logger.getLogger(ThriftActions.class);
private static final long SIZE_CHECK_EXTRA_DL = 50l * 1024l * 1024l;
/* *******************************************************************************
*
* LOGIN
*
* Login methods
*
* *******************************************************************************
*/
/**
* @param window the parentWindow
* @param data AuthenticationData as received from a successful login, or
* null if trying to resume a saved sessions
* @param forceCustomSatellite show satellite selection dialog even if there
* is just one
* @param loginWindow
* @return true if initialising the session worked, false otherwise
*/
public static boolean initSession(AuthenticationData data, boolean forceCustomSatellite, Window window) {
final boolean interactive = (data != null);
Client client = null;
WhoamiInfo whoami = null;
String address = null;
String satToken = null;
String masterToken = null;
if (interactive && !forceCustomSatellite && (data.satellites == null || data.satellites.isEmpty())) {
Gui.asyncMessageBox("Login erfolgreich, aber es wurde kein Satelliten-Server gefunden.\n"
+ " Bitte geben Sie die Adresse Ihres Servers manuell an.", MessageType.ERROR, LOGGER,
null);
}
do {
if (!interactive) {
// in session resume
SavedSession session = Config.getSavedSession();
if (session == null)
return false;
address = session.address;
satToken = session.token;
masterToken = session.masterToken;
} else {
// after global login, try to contact satellite
Satellite sat = null;
if (data.satellites != null && data.satellites.size() == 1 && !forceCustomSatellite) {
sat = data.satellites.get(0);
}
if (sat == null) {
sat = SatelliteListWindow.open(window, data.satellites);
if (sat == null)
return false;
}
if (sat.addressList == null || sat.addressList.isEmpty()) {
Gui.asyncMessageBox(
"Login erfolgreich, aber für den ausgewählten Satelliten-Server ist\n"
+ "keine Adresse hinterlegt. Kann nicht verbinden.", MessageType.ERROR,
LOGGER, null);
continue;
}
address = sat.addressList.get(0);
satToken = data.satelliteToken;
masterToken = data.masterToken;
}
// common for resume and fresh login
// try to get a new client
client = ThriftManager.getNewSatelliteClient(GraphicalCertHandler.getSslContext(address),
address, App.THRIFT_SSL_PORT, 5000);
// RPC version check
long remoteVersion = -1;
if (client != null) {
try {
remoteVersion = client.getVersion();
} catch (TTransportException e) {
} catch (TException e) {
remoteVersion = 1; // Assume something old
}
}
if (client == null || remoteVersion == -1) {
if (interactive) {
Gui.asyncMessageBox(
"Authentifizierung erfolgreich, die Verbindung zum Satelliten-Server ist jedoch nicht möglich.\n\n"
+ "Möglicherweise ist der Server nicht verfügbar, oder die Netzwerkverbindung gestört.",
MessageType.ERROR, null, null);
continue;
}
return false;
}
if (remoteVersion != Version.VERSION) {
if (interactive) {
Gui.asyncMessageBox("Das von Ihnen verwendete Dozentenmodul ist nicht mit dem"
+ " gewählten Satelliten-Server kompatibel.\n"
+ "Ihre Version: "+ Version.VERSION + "\n"
+ "Satelliten-Version: " + remoteVersion,
MessageType.ERROR, LOGGER, null);
continue;
}
return false;
}
// all good, try to get the whoami info
try {
whoami = client.whoami(satToken);
} catch (TAuthorizationException e) {
if (interactive) {
ThriftError.showMessage(window, LOGGER, e,
"Authentifizierung erfolgreich, der Satelliten-Server verweigert jedoch die Verbindung.\n"
+ "Versuchen Sie, sich erneut anzumelden.\n");
}
return false;
} catch (TException e) {
if (interactive) {
ThriftError.showMessage(window, LOGGER, e,
"Authentifizierung erfolgreich, bei der Kommunikation mit"
+ " dem Satelliten-Server trat jedoch ein interner Fehler auf.");
continue;
}
return false;
} catch (Exception e) {
if (interactive) {
Gui.asyncMessageBox("Unbekannter Fehler beim Verbinden mit dem Satelliten-Server.",
MessageType.ERROR, LOGGER, e);
continue;
}
return false;
}
} while (interactive && whoami == null);
if (whoami != null) {
Session.initialize(whoami, address, satToken, masterToken);
ThriftManager.setSatelliteAddress(
GraphicalCertHandler.getSslContext(Session.getSatelliteAddress()),
Session.getSatelliteAddress(), App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS);
QuickTimer.scheduleOnce(new Task() {
@Override
public void fire() {
// Cache useful data from server
MetaDataCache.getOperatingSystems();
MetaDataCache.getVirtualizers();
UserCache.getAll();
ImageCache.get(false);
LectureCache.get(false);
}
});
return true;
}
return false;
}
/* *******************************************************************************
*
* 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 imageBaseId image's id we are writing meta information of
* @param meta actual meta information as ImageBaseWrite
* @throws TException
* @throws TInvocationException
* @throws TNotFoundException
* @throws TAuthorizationException
*/
public static void updateImageBase(final String imageBaseId, final ImageBaseWrite meta)
throws TAuthorizationException, TNotFoundException, TInvocationException, TException {
ThriftManager.getSatClient().updateImageBase(Session.getSatelliteToken(), imageBaseId, meta);
}
/**
* GUI-BLOCKING Pushes the given permission map as custom permission for the
* given imageBaseId
*
* @param imageBaseId image's id we are writing permissions of
* @param permissions actual permissions map to write
* @throws TException
* @throws TInvocationException
* @throws TNotFoundException
* @throws TAuthorizationException
*/
public static void writeImagePermissions(final String imageBaseId,
final Map<String, ImagePermissions> permissions) throws TAuthorizationException,
TNotFoundException, TInvocationException, TException {
ThriftManager.getSatClient().writeImagePermissions(Session.getSatelliteToken(), imageBaseId,
permissions);
}
/* *******************************************************************************
*
* 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
*
* @param frame
* @param transferInformation
* @param versionInfo
* @throws TException
* @throws TInvocationException
* @throws TNotFoundException
* @throws TAuthorizationException
*/
public static void updateImageVersion(final String versionId, final ImageVersionWrite versionInfo)
throws TAuthorizationException, TNotFoundException, TInvocationException, TException {
ThriftManager.getSatClient().updateImageVersion(Session.getSatelliteToken(), versionId, versionInfo);
}
/* *******************************************************************************
*
* 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 Initialises 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 download
* @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 int osId, final long imageSize, final DownloadCallback callback) {
// TODO: Return value? Callback?
QFileChooser fc = new QFileChooser(Config.getDownloadPath(), true);
fc.setDialogTitle("Bitte wählen Sie einen Speicherort");
int action = fc.showSaveDialog(frame);
File selected = fc.getSelectedFile();
if (action != JFileChooser.APPROVE_OPTION || selected == null) {
if (callback != null)
callback.downloadInitialized(false);
return;
}
final File destDir = new File(selected, generateDirname(imageName, imageVersionId));
final File tmpDiskFile = new File(destDir.getAbsolutePath(), VmWrapper.generateFilename(imageName,
null) + ".part");
if (destDir.exists()) {
boolean ret = Gui.showMessageBox(frame, "Verzeichnis '" + destDir.getAbsolutePath()
+ "' existiert bereits, wollen Sie die VM darin überschreiben?",
MessageType.QUESTION_YESNO, LOGGER, null);
if (!ret) {
// user aborted
if (callback != null)
callback.downloadInitialized(false);
return;
}
// delete it
if (!tmpDiskFile.delete() && tmpDiskFile.exists()) {
Gui.showMessageBox(frame, "Datei konnte nicht überschrieben werden!", MessageType.ERROR,
LOGGER, null);
if (callback != null)
callback.downloadInitialized(false);
return;
}
} else {
destDir.getAbsoluteFile().mkdirs();
}
// Check the free space on disk
if (destDir.getUsableSpace() < imageSize + SIZE_CHECK_EXTRA_DL) {
Gui.showMessageBox(frame, "Nicht genügend Speicherplatz im ausgewählten Verzeichnis verfügbar.\n"
+ "Brauche: " + FormatHelper.bytes(imageSize + SIZE_CHECK_EXTRA_DL, false) + "\n"
+ "Habe: " + FormatHelper.bytes(destDir.getUsableSpace(), false), MessageType.ERROR,
LOGGER, null);
if (callback != null)
callback.downloadInitialized(false);
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;
}
final DownloadTask dlTask;
try {
dlTask = new DownloadTask(Session.getSatelliteAddress(), transInf.getPlainPort(),
transInf.getToken(), tmpDiskFile, imageSize, null);
} catch (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;
}
dlTask.addListener(new TransferEventListener() {
@Override
public void update(TransferEvent event) {
if (event.state != TransferState.FINISHED)
return;
DiskImage diskImage = null;
String ext = virtualizerId;
try {
diskImage = new DiskImage(tmpDiskFile);
} catch (IOException | UnknownImageFormatException e) {
LOGGER.warn("Could not open downloaded image for analyze step", e);
}
if (diskImage != null) {
if (diskImage.format != null) {
ext = diskImage.format.extension;
}
if (diskImage.isCompressed) {
Gui.asyncMessageBox("Die heruntergeladene VM '" + imageName + "' ist ein"
+ "\nkomprimiertes Abbild. Sie müssen das Abbild dekomprimieren,"
+ "\nbevor Sie es verändern können."
+ "\n\n(TODO: Hinweis vmware disk tools)", MessageType.WARNING, null,
null); // TODO
}
}
File destImage = new File(destDir.getAbsolutePath(), VmWrapper.generateFilename(
imageName, ext));
destImage.delete();
if (!tmpDiskFile.renameTo(destImage)) {
destImage = tmpDiskFile; // Must be Windows...
}
try {
VmWrapper.wrapVm(destImage, imageName, transInf.getMachineDescription(),
virtualizerId, osId, diskImage);
} catch (MetaDataMissingException | IOException e) {
Gui.asyncMessageBox(
"Zur heruntergeladenen VM konnte keine vmx-Datei angelegt werden."
+ "\nSie können versuchen, das Abbild manuell in den VMWare-Player zu importieren.",
MessageType.WARNING, LOGGER, e);
}
}
});
Gui.asyncExec(new Runnable() {
@Override
public void run() {
MainWindow.addDownload(imageName, tmpDiskFile.getName(), dlTask);
}
});
new Thread(dlTask).start();
Config.setDownloadPath(destDir.getParentFile().getAbsolutePath());
if (callback != null)
callback.downloadInitialized(true);
}
});
}
/**
* Helper to generate a directory name for the given imageName and
* imageVersionId.
* Uses the given parameters to build a hopefully unique name as it takes
* the first 8
* chars of the version id...
*
* @param imageName name of the image
* @param imageVersionId version id
* @return generated directory name as String
*/
private static String generateDirname(String imageName, String imageVersionId) {
String fileName = imageName.replaceAll("[^a-zA-Z0-9_\\.\\-]+", "_");
if (fileName.length() > 50) {
fileName = fileName.substring(0, 50);
}
fileName += "--" + imageVersionId.substring(0, 8);
return fileName;
}
/* *******************************************************************************
*
* IMAGE METADATA
*
* *******************************************************************************/
/**
* Callback interface for image meta calls
*/
public interface ImageMetaCallback {
void fetchedImageDetails(ImageDetailsRead details, Map<String, ImagePermissions> permissions);
}
/**
* BLOCKING Gets the image details (w/o permissions) of the given
* imageBaseID
*
* @param frame to display user feedback on
* @param imageBaseId image's id to get the details of
* @return details as ImageDetailsRead if fetching worked, null otherwise
*/
public static ImageDetailsRead getImageDetails(final Frame frame, final String imageBaseId) {
ImageDetailsRead details = null;
try {
details = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(), imageBaseId);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Lesen der Metadaten");
}
return details;
}
/**
* NON-BLOCKING Gets the image details (without permissions) of the given
* imageBaseId.
* Will return the details (or null if fetching failed) back to the
* gui-thread
* through the given callback
*
* @param frame to display user feedback on
* @param imageBaseId image's id to get the details of
* @param callback interface to return the details back to the gui
*/
public static void getImageDetails(final Frame frame, final String imageBaseId,
final ImageMetaCallback callback) {
QuickTimer.scheduleOnce(new Task() {
ImageDetailsRead details = null;
@Override
public void fire() {
details = ThriftActions.getImageDetails(frame, imageBaseId);
Gui.asyncExec(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.fetchedImageDetails(details, null);
}
}
});
}
});
}
/**
* NON-BLOCKING Gets the image details and the user-specific permission list
* of
* the given imageBaseId. Will return the objects through the given callback
*
* @param frame to display user feedback on
* @param imageBaseId image's id to get the full details of
* @param callback interface called to return the details back to the gui
*/
public static void getImageFullDetails(final Frame frame, final String imageBaseId,
final ImageMetaCallback callback) {
QuickTimer.scheduleOnce(new Task() {
ImageDetailsRead details = null;
Map<String, ImagePermissions> permissions = null;
@Override
public void fire() {
details = ThriftActions.getImageDetails(frame, imageBaseId);
permissions = ThriftActions.getImagePermissions(frame, imageBaseId);
Gui.asyncExec(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.fetchedImageDetails(details, permissions);
}
}
});
}
});
}
/**
* NON-BLOCKING Gets the user-specific permission list for the given
* imageBaseId
*
* @param frame to display user feedback on
* @param imageBaseId image's id to get the permission list of
* @param callback interface to return the permission list back to the gui
*/
public static void getImagePermissions(final Frame frame, final String imageBaseId,
final ImageMetaCallback callback) {
QuickTimer.scheduleOnce(new Task() {
Map<String, ImagePermissions> permissionMap = null;
@Override
public void fire() {
permissionMap = ThriftActions.getImagePermissions(frame, imageBaseId);
Gui.asyncExec(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.fetchedImageDetails(null, permissionMap);
}
}
});
}
});
}
/**
* BLOCKING Gets the user-specific permission list for the given imageBaseId
*
* @param frame to display user feedback on
* @param imageBaseId image's base id to get the user permissions of
* @return the map userid-permissions if fetching worked, null otherwise
*/
public static Map<String, ImagePermissions> getImagePermissions(final Frame frame,
final String imageBaseId) {
Map<String, ImagePermissions> permissionMap = null;
try {
permissionMap = ThriftManager.getSatClient().getImagePermissions(Session.getSatelliteToken(),
imageBaseId);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Lesen der Metadaten");
}
return permissionMap;
}
/**
* BLOCKING Sets the owner of the given lectureId to newOwner
*
* @param frame to display user feedback on
* @param lectureId lecture's id to set the new owner of
* @param newOwner as UserInfo
* @return true if it worked, false otherwise
*/
public static boolean setImageOwner(final Frame frame, final String lectureId, final UserInfo newOwner) {
try {
ThriftManager.getSatClient().setImageOwner(Session.getSatelliteToken(), lectureId,
newOwner.getUserId());
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Übertragen der Besitzrechte");
return false;
}
return true;
}
/* *******************************************************************************
*
* IMAGE / VERSION DELETION
*
* *******************************************************************************/
/**
* 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);
}
/**
* BLOCKING Deletes the base image with the given id. Will call the given
* callback to report about the status of this operation
*
* @param frame to display user feedback on
* @param imageBaseId image base id to delete
* @param callback interface to report the outcome to the gui
*/
public static void deleteImageBase(final Frame frame, final String imageBaseId,
final DeleteCallback callback) {
if (imageBaseId == null || imageBaseId.isEmpty())
return;
// first look if we have versions
ImageDetailsRead details = null;
// these help construct the question text for the user, bit ugly...
List<LectureSummary> lecturesToBeDeleted = null;
List<ImageVersionDetails> versionToBeDeleted = null;
try {
details = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(), imageBaseId);
List<LectureSummary> lectureList = ThriftManager.getSatClient().getLectureList(
Session.getSatelliteToken(), 100);
for (LectureSummary lecture : lectureList) {
if (imageBaseId.equals(lecture.getImageBaseId())) {
if (lecturesToBeDeleted == null)
lecturesToBeDeleted = new ArrayList<LectureSummary>();
lecturesToBeDeleted.add(lecture);
}
}
for (ImageVersionDetails version : details.getVersions()) {
if (version.isValid) {
if (versionToBeDeleted == null)
versionToBeDeleted = new ArrayList<ImageVersionDetails>();
versionToBeDeleted.add(version);
}
}
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e,
"Fehler beim Holen der Versionen/Veranstaltung zu folgendes Image: " + imageBaseId);
return;
}
String questionText;
if (versionToBeDeleted != null && !versionToBeDeleted.isEmpty()) {
questionText = "Dieses Image hat folgende gültige Versionen:\n";
for (ImageVersionDetails version : versionToBeDeleted) {
questionText += version.getVersionId() + "\n";
}
questionText += "\n";
} else {
questionText = "";
}
if (lecturesToBeDeleted != null && !lecturesToBeDeleted.isEmpty()) {
questionText += "Dieses Image ist mit folgenden Veranstaltungen verknüpft:\n";
for (LectureSummary lecture : lecturesToBeDeleted) {
questionText += lecture.getLectureName() + "\n";
}
questionText += "\n";
}
questionText += "Wollen Sie dieses Image wirklich löschen?";
if (!userConfirmed(frame, questionText))
return;
try {
ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), imageBaseId);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Konnte Basis-Image nicht löschen!");
}
}
/**
* 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 deleteImageVersion(final Frame frame, final String imageVersionId,
final DeleteCallback callback) {
if (imageVersionId == null || imageVersionId.isEmpty())
return;
boolean success = false;
List<LectureSummary> lectureList = null;
try {
// fetch lectures
lectureList = ThriftManager.getSatClient().getLectureList(Session.getSatelliteToken(), 100);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Holen der Liste der Veranstaltungen");
if (callback != null)
callback.isDeleted(success);
return;
}
String questionText = "";
// represents if we found matches of not, needed to make a proper
// message
boolean matches = false;
if (lectureList != null && !lectureList.isEmpty()) {
for (LectureSummary lecture : lectureList) {
if (imageVersionId.equals(lecture.getImageVersionId())) {
if (!matches)
questionText = "Diese Version ist zu folgende Veranstaltungen verknüpft:\n";
matches = true;
questionText += lecture.getLectureName() + "\n";
}
}
if (matches)
questionText += "\nWollen Sie diese Version samt Veranstaltungen löschen?\n";
}
if (!matches)
questionText = "Wollen Sie diese Image-Version wirklich löschen?";
if (!userConfirmed(frame, questionText))
return;
try {
ThriftManager.getSatClient().deleteImageVersion(Session.getSatelliteToken(), imageVersionId);
LOGGER.info("Deleted version '" + imageVersionId + "'.");
success = true;
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Löschen der Version");
if (callback != null)
callback.isDeleted(success);
return;
}
final boolean fSuccess = success;
Gui.asyncExec(new Runnable() {
@Override
public void run() {
if (callback != null)
callback.isDeleted(fSuccess);
}
});
}
/* *******************************************************************************
*
* LECTURE CREATION
*
* *******************************************************************************/
/**
* BLOCKING 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;
}
/**
* BLOCKING 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 boolean 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");
return false;
}
return true;
}
/* *******************************************************************************
*
* LECTURE METADATA
*
* *******************************************************************************/
/**
* Interface for GUI-classes running async lecture meta calls
*/
public interface LectureMetaCallback {
void fetchedLectureAndImageDetails(LectureRead lecture, ImageDetailsRead image);
}
/**
* NON-BLOCKING Gets the lecture and image details of the given lectureId.
* IF the lecture does not exist, both fields returned will be null. If the
* lecture exists, the image can still be null, since not all lectures link
* to an image at all times.
*
* @param frame to display user feedback on
* @param lectureId lecture to get the full details of
* @param callback interface to return the details to the gui
*/
public static void getLectureAndImageDetails(final Frame frame, final String lectureId,
final LectureMetaCallback callback) {
QuickTimer.scheduleOnce(new Task() {
@Override
public void fire() {
final LectureRead lecture = ThriftActions.getLectureDetails(frame, lectureId);
final ImageDetailsRead fImage;
if (lecture != null && lecture.imageBaseId != null) {
fImage = ThriftActions.getImageDetails(frame, lecture.getImageBaseId());
} else {
fImage = null;
}
Gui.asyncExec(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.fetchedLectureAndImageDetails(lecture, fImage);
}
}
});
}
});
}
/**
* BLOCKING Gets the lecture details of the lecture with given lectureId
*
* @param frame to display user feedback on
* @param lectureId lecture to get the details of
* @return LectureRead if fetching details worked, null otherwise
*/
public static LectureRead getLectureDetails(final Frame frame, final String lectureId) {
LectureRead lecture = null;
try {
lecture = ThriftManager.getSatClient().getLectureDetails(Session.getSatelliteToken(), lectureId);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Konnte Veranstaltungdaten nicht abrufen");
}
return lecture;
}
/**
* BLOCKING Updates the lecture with given lectureId to the given lecture
*
* @param frame to show user feedback on
* @param lectureId lecture's is to update
* @param lecture LectureWrite data to update the lecture with
* @return
*/
public static boolean updateLecture(final Frame frame, final String lectureId,
final LectureWrite lectureWrite) {
try {
ThriftManager.getSatClient().updateLecture(Session.getSatelliteToken(), lectureId, lectureWrite);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Updaten der Veranstaltung");
return false;
}
return true;
}
/**
* BLOCKING Gets the list of user-specific permission for the lecture with
* the
* given lectureId
*
* @param frame to display user feedback on
* @param lectureId lecture's id to get the permission list of
* @return the map of the userid-permissions if fetching worked, null
* otherwise
*/
public static Map<String, LecturePermissions> getLecturePermissions(final Frame frame,
final String lectureId) {
Map<String, LecturePermissions> permissions = null;
try {
permissions = ThriftManager.getSatClient().getLecturePermissions(Session.getSatelliteToken(),
lectureId);
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Konnte Veranstaltungdaten nicht abrufen");
}
return permissions;
}
/**
* BLOCKING Sets the owner of the given lectureId to newOwner
*
* @param frame to display user feedback on
* @param lectureId lecture's id to set the new owner of
* @param newOwner as UserInfo
* @return true if it worked, false otherwise
*/
public static boolean setLectureOwner(final Frame frame, final String lectureId, final UserInfo newOwner) {
try {
ThriftManager.getSatClient().setLectureOwner(Session.getSatelliteToken(), lectureId,
newOwner.getUserId());
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Übertragen der Besitzrechte");
return false;
}
return true;
}
/* *******************************************************************************
*
* LECTURE DELETION
*
* *******************************************************************************/
/**
* Callback interface reporting the status of the deletion back to the gui
*/
public interface DeleteLectureCallback {
void deleted(boolean success);
}
/**
* NON-BLOCKING Deletes the lecture with the given lectureId
*
* @param frame to display user feedback on
* @param lectureId id of the lecture to delete
* @param callback interface to report the status back to the gui
*/
public static void deleteLecture(final Frame frame, final String lectureId,
final DeleteLectureCallback callback) {
if (lectureId == null)
return;
if (!userConfirmed(frame, "Wollen Sie diese Veranstaltung wirklick löschen?"))
return;
QuickTimer.scheduleOnce(new Task() {
boolean success = false;
@Override
public void fire() {
try {
ThriftManager.getSatClient().deleteLecture(Session.getSatelliteToken(), lectureId);
success = true;
} catch (TException e) {
ThriftError.showMessage(frame, LOGGER, e, "Konnte Veranstaltungdaten nicht abrufen");
}
Gui.asyncExec(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.deleted(success);
}
}
});
}
});
}
/* *******************************************************************************
*
* 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, null, null);
}
}