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.HashMap; import java.util.Iterator; 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.ImagePublishData; 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.Branding; 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; /* ******************************************************************************* * * SESSION * * Session initialization * * ******************************************************************************* */ /** * @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; long remoteVersion = -1; if (interactive && !forceCustomSatellite && (data.satellites == null || data.satellites.isEmpty())) { Gui.asyncMessageBox("Login erfolgreich, aber es wurde kein Satellitenserver 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 { // determine which sat to connect to // first check if a sat selection was forced Satellite sat = null; if (forceCustomSatellite) { sat = SatelliteListWindow.open(window, data.satellites); if (sat == null) return false; } // not forced, do regular sat processing if (sat == null) { // if we received only one sat, use that one if (data.satellites != null && data.satellites.size() == 1) { sat = data.satellites.get(0); } // we had more than one, so check the list if (sat == null) { if (!forceCustomSatellite) { // Remove testing servers (those starting with {x}) if shift was not pressed for (Iterator it = data.satellites.iterator(); it.hasNext();) { Satellite entry = it.next(); if (entry.displayName != null && entry.displayName.startsWith("{x}")) { it.remove(); } } } // after removing the test sats, check if we have only one left and directly use that // instead of asking the user if (data.satellites.size() != 1) { sat = SatelliteListWindow.open(window, data.satellites); if (sat == null) return false; } else { sat = data.satellites.get(0); } } } if (sat.addressList == null || sat.addressList.isEmpty()) { Gui.asyncMessageBox( "Login erfolgreich, aber für den ausgewählten Satellitenserver ist\n" + "keine Adresse hinterlegt. Kann nicht verbinden.", MessageType.ERROR, LOGGER, null); continue; } address = sat.addressList.get(0); satToken = data.satelliteToken; masterToken = data.masterToken; // In case we loop, make sure the window is shown on the second iteration forceCustomSatellite = true; } // 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 if (client != null) { try { remoteVersion = client.getVersion(Version.VERSION); } 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 Satellitenserver ist jedoch nicht möglich.\n\n" + "Möglicherweise ist der Server nicht verfügbar, oder die Netzwerkverbindung gestört.", MessageType.ERROR, null, null); if (data.satellites.size() == 1) { return false; } continue; } return false; } if (remoteVersion < Version.MIN_VERSION || remoteVersion > Version.VERSION) { if (interactive) { Gui.asyncMessageBox("Das von Ihnen verwendete Dozentenmodul ist nicht mit dem" + " gewählten Satellitenserver 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 Satellitenserver 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 Satellitenserver trat jedoch ein interner Fehler auf."); continue; } return false; } catch (Exception e) { if (interactive) { Gui.asyncMessageBox("Unbekannter Fehler beim Verbinden mit dem Satellitenserver.", MessageType.ERROR, LOGGER, e); continue; } return false; } } while (interactive && whoami == null); if (whoami != null) { Session.initialize(whoami, address, satToken, masterToken, remoteVersion); 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 der VM 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 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() { // since this function (initDownload) supports both download from the satellite and from the masterserver // we simply try to download from the sat first and if that fails, we try to download from the masterserver // this has the nice side effect, that if a download from the masterserver is requested, it tries // to download that image from the sat first (which is probably faster than from the masterserver) TException transEx = null; String transHost = null; TransferInformation transInf = null; try { transInf = ThriftManager.getSatClient().requestDownload(Session.getSatelliteToken(), imageVersionId); transHost = Session.getSatelliteAddress(); } catch (TException e) { transEx = e; } if (transInf == null) { // satellite denied download, try master transHost = null; try { transInf = ThriftManager.getMasterClient().downloadImage(Session.getSatelliteToken(), imageVersionId); transHost = App.getMasterServerAddress(); } catch (TException e) { transEx = e; } } if (transInf == null) { // both download request failed, show user feedback ThriftError.showMessage(frame, LOGGER, transEx, "Die Download-Anfrage ist gescheitert"); if (callback != null) callback.downloadInitialized(false); return; } final String fTransHost = transHost; final TransferInformation fTransInf = transInf; final DownloadTask dlTask; try { dlTask = new DownloadTask(fTransHost, 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.setMinConnections(Config.getTransferConnectionCount()); 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) { String msg = "Die heruntergeladene VM '" + imageName + "' ist ein komprimiertes " + "Abbild.
Sie müssen das Abbild dekomprimieren, bevor Sie es verändern " + "können.
Die VM wird lokal voraussichtlich nicht startfähig sein!" + "

Bitte lesen Sie die Hinweise unter " + "" + "VMDK Disk Types"; Gui.asyncMessageBox(msg, MessageType.WARNING, null, null); } } 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, fTransInf.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 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); } } }); } }); } /** * BLOCKING Gets the data for image with UUID imageBaseId * * @param imageBaseId * @return ImagePublishData image's data if sucessful, null otherwise. */ public static ImagePublishData getImageData(final String imageBaseId) { ImagePublishData data = null; try { data = ThriftManager.getMasterClient().getImageData(Session.getSatelliteToken(), imageBaseId); } catch (TException e) { LOGGER.error("Could not query sat for ImagePublishData for '" + imageBaseId + "':", e); } return data; } /** * 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 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 getImagePermissions(final Frame frame, final String imageBaseId) { Map 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 */ public static void deleteImageBase(final Frame frame, final String imageBaseId) { 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 lecturesToBeDeleted = null; List versionToBeDeleted = null; try { details = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(), imageBaseId); List lectureList = ThriftManager.getSatClient().getLectureList( Session.getSatelliteToken(), 100); for (LectureSummary lecture : lectureList) { if (imageBaseId.equals(lecture.getImageBaseId())) { if (lecturesToBeDeleted == null) lecturesToBeDeleted = new ArrayList(); lecturesToBeDeleted.add(lecture); } } for (ImageVersionDetails version : details.getVersions()) { if (version.isValid) { if (versionToBeDeleted == null) versionToBeDeleted = new ArrayList(); versionToBeDeleted.add(version); } } } catch (TException e) { ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Holen der Versionen/Veranstaltung zu folgender VM: " + imageBaseId); return; } String questionText; if (versionToBeDeleted != null && !versionToBeDeleted.isEmpty()) { questionText = "Die VM \"" + details.getImageName() + "\" hat folgende gültige Versionen:\n"; for (ImageVersionDetails version : versionToBeDeleted) { questionText += version.getVersionId() + "\n"; } questionText += "\n"; } else { questionText = ""; } if (lecturesToBeDeleted != null && !lecturesToBeDeleted.isEmpty()) { questionText += "Folgende Veranstaltungen sind mit dieser VM verknüpft: \n"; for (LectureSummary lecture : lecturesToBeDeleted) { questionText += lecture.getLectureName() + "\n"; } questionText += "\n"; } questionText += "Wollen Sie wirklich mit dem Löschen fortfahren?"; if (!userConfirmed(frame, questionText)) return; try { ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), imageBaseId); } catch (TException e) { ThriftError.showMessage(frame, LOGGER, e, "Konnte VM-Daten 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 version 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 ImageVersionDetails version, final DeleteCallback callback) { if (version == null || version.versionId == null || version.versionId.isEmpty()) return; String versionId = version.versionId; boolean success = false; List 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 (versionId.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 die VM-Image-Version vom " + FormatHelper.shortDate(version.createTime) + " Uhr wirklich löschen?"; if (!userConfirmed(frame, questionText)) return; try { ThriftManager.getSatClient().deleteImageVersion(Session.getSatelliteToken(), versionId); LOGGER.info("Deleted version '" + versionId + "'."); 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 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 getLecturePermissions(final Frame frame, final String lectureId) { Map 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(Map failedLectures); } /** * NON-BLOCKING Deletes the lectures of the given list * * @param frame to display user feedback on * @param list of lectures to be deleted * @param callback interface to report the lectures, which haven't been * deleted. */ public static void deleteLecture(final Frame frame, final List lectures, final DeleteLectureCallback callback) { if (lectures == null) return; String messageText = lectures.size() == 1 ? "Wollen Sie diese Veranstaltung wirklich löschen?" : "Wollen Sie die " + lectures.size() + " Veranstaltungen wirklich löschen?"; if (!userConfirmed(frame, messageText)) return; QuickTimer.scheduleOnce(new Task() { Map failedLectures = new HashMap(); @Override public void fire() { for (final LectureSummary l : lectures) { if (l == null) continue; try { ThriftManager.getSatClient().deleteLecture(Session.getSatelliteToken(), l.lectureId); } catch (TException e) { failedLectures.put(l, e); } } Gui.asyncExec(new Runnable() { @Override public void run() { if (callback != null) { callback.deleted(failedLectures); } } }); } }); } /* ******************************************************************************* * * PUBLIC IMAGES * * *******************************************************************************/ /** * BLOCKING Gets the image details of an image on the masterserver * * @param imageVersionId * @return ImageDetailsRead if fetching the details worked, null otherwise. */ public static ImageDetailsRead getPublishedImageDetails(final String imageBaseId) { ImageDetailsRead data = null; try { data = ThriftManager.getMasterClient().getImageDetails(Session.getMasterToken(), imageBaseId); } catch (TException e) { LOGGER.error("Could not query masterserver for ImageDetailsRead for version '" + imageBaseId + "':", e); } return data; } /* ******************************************************************************* * * 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); } }