diff options
author | Simon Rettberg | 2015-10-01 20:00:27 +0200 |
---|---|---|
committer | Simon Rettberg | 2015-10-01 20:00:27 +0200 |
commit | 1fd45f1ec985012179b26c8136eadf0c5ae3c2c8 (patch) | |
tree | 05f2c0065d4e837f896cd3b0668125e87a211806 /dozentenmodul/src | |
parent | [client] Fix escape not working in message boxes (diff) | |
download | tutor-module-1fd45f1ec985012179b26c8136eadf0c5ae3c2c8.tar.gz tutor-module-1fd45f1ec985012179b26c8136eadf0c5ae3c2c8.tar.xz tutor-module-1fd45f1ec985012179b26c8136eadf0c5ae3c2c8.zip |
[client] Start upload only when finishing wizard, add final summary page to wizard
Diffstat (limited to 'dozentenmodul/src')
19 files changed, 810 insertions, 362 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/filetransfer/AsyncHashGenerator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/filetransfer/AsyncHashGenerator.java index f53d5d0b..83fbb212 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/filetransfer/AsyncHashGenerator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/filetransfer/AsyncHashGenerator.java @@ -23,7 +23,7 @@ public class AsyncHashGenerator extends Thread { private static final Logger LOGGER = Logger.getLogger(AsyncHashGenerator.class); - private final String uploadToken; + private String uploadToken = null; private final RandomAccessFile file; private final List<ByteBuffer> blockHashes; private final MessageDigest digester; @@ -32,9 +32,7 @@ public class AsyncHashGenerator extends Thread { private volatile boolean isCanceled = false; - public AsyncHashGenerator(String uploadToken, File uploadFile) throws FileNotFoundException, - NoSuchAlgorithmException { - this.uploadToken = uploadToken; + public AsyncHashGenerator(File uploadFile) throws FileNotFoundException, NoSuchAlgorithmException { try { digester = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e1) { @@ -54,6 +52,13 @@ public class AsyncHashGenerator extends Thread { setName("HashGenerator"); } + public void setUploadToken(String token) { + if (!isCanceled && this.uploadToken == null) { + this.uploadToken = token; + submitHashes(); + } + } + @Override public void run() { Checker checker = new Checker(); @@ -92,7 +97,6 @@ public class AsyncHashGenerator extends Thread { } } finally { Util.safeClose(file); - LOGGER.debug("AsyncHashGenerator done"); } } @@ -147,20 +151,16 @@ public class AsyncHashGenerator extends Thread { isCanceled = true; break; } - blockHashes.add(ByteBuffer.wrap(hash)); - LOGGER.debug("blockIndex=" + blockIndex + ", list.size()=" + list.size()); - if (blockIndex <= 5 || blockIndex % 20 == 0 || blockIndex + 1 == list.size()) { - try { - ThriftManager.getSatClient().updateBlockHashes(uploadToken, blockHashes); - } catch (TInvalidTokenException e) { - LOGGER.warn("Cannot send hashList to satellite: Sat claims uploadToken is invalid!"); - isCanceled = true; + synchronized (blockHashes) { + blockHashes.add(ByteBuffer.wrap(hash)); + } + if (blockIndex % 20 == 0 || blockIndex + 1 == list.size()) { + if (!submitHashes()) break; - } catch (TException e) { - LOGGER.warn("Unknown exception when submitting hashList to sat", e); - } - if (blockIndex + 1 == list.size()) + if (blockIndex + 1 == list.size()) { + LOGGER.debug("Hashing done"); break; + } } } } catch (InterruptedException e) { @@ -168,9 +168,30 @@ public class AsyncHashGenerator extends Thread { interrupt(); } finally { thisGenerator.interrupt(); - LOGGER.debug("Hasher done"); } } } + /** + * Submit current list of hashes. + * + * @return false if the token is not known to the server + */ + private boolean submitHashes() { + synchronized (blockHashes) { + if (uploadToken == null) // No token yet, cannot submit + return true; + try { + ThriftManager.getSatClient().updateBlockHashes(uploadToken, blockHashes); + } catch (TInvalidTokenException e) { + LOGGER.warn("Cannot send hashList to satellite: Sat claims uploadToken is invalid!"); + isCanceled = true; + return false; + } catch (TException e) { + LOGGER.warn("Unknown exception when submitting hashList to sat", e); + } + return true; + } + } + } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java index b5844630..66e2fa60 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java @@ -257,7 +257,8 @@ public class Gui { * Generic helper to show a message box to the user, and optionally log the * message to the log file. * - * @param parent partent window (used for positioning) + * @param parent partent window (used for positioning/modality). If null, + * the active window will be used * @param message Message to display. Can be multi line. * @param messageType Type of message (warning, information) * @param logger Logger instance to log to. Can be null. @@ -278,9 +279,10 @@ public class Gui { } } - if (exception != null) + if (exception != null) { message += "\n\n" + exception.getClass().getSimpleName() + "\n" + exception.getMessage() + "\n" - + " (Siehe Logdatei)"; + + " (Für Stack-Trace siehe Logdatei)"; + } if (messageType.buttons == -1) { JOptionPane.showMessageDialog(parent, message, messageType.title, messageType.optionPaneId); return true; @@ -292,6 +294,21 @@ public class Gui { } /** + * Generic helper to show a message box to the user, and optionally log the + * message to the log file. + * + * @param message Message to display. Can be multi line. + * @param messageType Type of message (warning, information) + * @param logger Logger instance to log to. Can be null. + * @param exception Exception related to this message. Can be null. + * @return true if OK or YES was clicked, false for CANCEL/NO/(X) + */ + public static boolean showMessageBox(String message, MessageType messageType, Logger logger, + Throwable exception) { + return showMessageBox(null, message, messageType, logger, exception); + } + + /** * Show a message box to the user asynchronously, and optionally log the * message to the log file. This is most useful when working from another * thread. diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java index 3577d1ba..b396b35a 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java @@ -82,6 +82,7 @@ public abstract class MainWindow { * * @param clazz */ + @SuppressWarnings("unchecked") public static <T extends CompositePage> T showPage(Class<T> clazz) { if (currentPage != null) { if (!currentPage.requestHide()) { diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/activity/UploadPanel.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/activity/UploadPanel.java index 0c588656..e8313120 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/activity/UploadPanel.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/activity/UploadPanel.java @@ -7,7 +7,6 @@ import org.apache.log4j.Logger; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.state.UploadWizardState; -import org.openslx.thrifthelper.ThriftManager; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; @@ -21,33 +20,27 @@ public class UploadPanel extends TransferPanel { private final UploadPanel panel = this; public UploadPanel(UploadWizardState state) { - super(state.uploadTask, state.name, state.diskFile.getName()); + super(state.upload.getUploadTask(), state.name, state.diskFile.getName()); btnClose.addActionListener(new ButtonAction()); this.state = state; - state.uploadTask.addListener(this); + state.upload.getUploadTask().addListener(this); } private class ButtonAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { - if (!state.uploadTask.isCanceled()) { + if (!state.upload.getUploadTask().isCanceled()) { if (!Gui.showMessageBox(panel, "Wollen Sie diesen Transfer wirklich abbrechen?", MessageType.QUESTION_YESNO, null, null)) return; - state.uploadTask.cancel(); - state.hashGen.cancel(); QuickTimer.scheduleOnce(new Task() { @Override public void fire() { - try { - ThriftManager.getSatClient().cancelUpload(state.transferInformation.getToken()); - } catch (Exception ex) { - LOGGER.debug("Remote error while canceling upload for " + state.uuid, ex); - } + state.upload.cancelError(); } }); } - state.uploadTask.removeListener(panel); + state.upload.getUploadTask().removeListener(panel); close(); } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java index 33c51111..e5bd99ad 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java @@ -19,11 +19,11 @@ import java.util.Map.Entry; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import javax.swing.RowFilter; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; 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; @@ -36,7 +36,6 @@ import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.MainWindow; -import org.openslx.dozmod.gui.control.table.ListTable.ListModel; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.gui.helper.PopupMenu; import org.openslx.dozmod.gui.helper.UiFeedback; @@ -49,6 +48,7 @@ import org.openslx.dozmod.thrift.Session; import org.openslx.dozmod.thrift.ThriftActions; import org.openslx.dozmod.thrift.ThriftActions.DeleteCallback; import org.openslx.dozmod.thrift.ThriftActions.ImageMetaCallback; +import org.openslx.dozmod.thrift.ThriftError; import org.openslx.dozmod.thrift.cache.LectureCache; import org.openslx.dozmod.thrift.cache.MetaDataCache; import org.openslx.dozmod.thrift.cache.UserCache; @@ -393,22 +393,26 @@ public class ImageDetailsWindow extends ImageDetailsWindowLayout implements UiFe cboShareMode.getItemAt(cboShareMode.getSelectedIndex())); // now trigger the actual action if (metadataChanged) { - if (ThriftActions.updateImageBase(JOptionPane.getFrameForComponent(me), image.getImageBaseId(), - ibw)) { - LOGGER.info("Successfully saved new metadata"); - metadataChanged = false; - } else { + try { + ThriftActions.updateImageBase(image.getImageBaseId(), ibw); + } catch (TException e) { + ThriftError.showMessage(me, LOGGER, e, + "Konnte aktualisierte Metadaten nicht an den Server übermitteln"); return false; } + LOGGER.info("Successfully saved new metadata"); + metadataChanged = false; } if (permissionsChanged) { - if (ThriftActions.writeImagePermissions(JOptionPane.getFrameForComponent(me), - image.getImageBaseId(), customPermissions)) { - LOGGER.info("Successfully saved new permissions"); - permissionsChanged = false; - } else { + try { + ThriftActions.writeImagePermissions(image.getImageBaseId(), customPermissions); + } catch (TException e) { + ThriftError.showMessage(me, LOGGER, e, + "Konnte geänderte Berechtigungen nicht an den Server übermitteln"); return false; } + LOGGER.info("Successfully saved new permissions"); + permissionsChanged = false; } return true; } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java index 72670b83..3c7892c2 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java @@ -44,7 +44,7 @@ public abstract class ImageDetailsWindowLayout extends JDialog { protected final PersonLabel lblUpdater; protected final QLabel lblUpdateTime; - protected final JComboBox<OperatingSystem> cboOperatingSystem; + protected final ComboBox<OperatingSystem> cboOperatingSystem; protected final QLabel lblVirtualizer; protected final JTextField txtTags; protected final JCheckBox chkIsTemplate; diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageCreationWizard.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageCreationWizard.java index 971c0471..ed6e30da 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageCreationWizard.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageCreationWizard.java @@ -14,9 +14,12 @@ import org.openslx.dozmod.gui.helper.UiFeedback; import org.openslx.dozmod.gui.wizard.page.ImageCustomPermissionPage; import org.openslx.dozmod.gui.wizard.page.ImageMetaDataPage; import org.openslx.dozmod.gui.wizard.page.ImageUploadPage; +import org.openslx.dozmod.gui.wizard.page.ImageUploadSummaryPage; import org.openslx.dozmod.state.UploadWizardState; import org.openslx.dozmod.thrift.Session; import org.openslx.dozmod.thrift.ThriftActions; +import org.openslx.dozmod.thrift.ThriftError; +import org.openslx.dozmod.thrift.UploadInitiator.GotUploadTokenCallback; import org.openslx.thrifthelper.ThriftManager; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; @@ -26,10 +29,12 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { private final static Logger LOGGER = Logger.getLogger(ImageCreationWizard.class); - private final UploadWizardState uploadWizardState = new UploadWizardState(); + private final UploadWizardState state = new UploadWizardState(); protected ImageUploadPage imageUploadPage; protected ImageMetaDataPage imageMetaDataPage; protected ImageCustomPermissionPage imageCustomPermissionPage; + private boolean baseWritten = false; + private boolean permissionsWritten = false; /** * Wizard for creating or editing an image @@ -38,11 +43,11 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { */ public ImageCreationWizard(Window parent) { super(parent); - uploadWizardState.defaultPermissions = Session.getSatelliteConfig().defaultImagePermissions; + state.defaultPermissions = Session.getSatelliteConfig().defaultImagePermissions; - imageUploadPage = new ImageUploadPage(this, uploadWizardState, null); - imageMetaDataPage = new ImageMetaDataPage(this, uploadWizardState); - imageCustomPermissionPage = new ImageCustomPermissionPage(this, uploadWizardState); + imageUploadPage = new ImageUploadPage(this, state, null); + imageMetaDataPage = new ImageMetaDataPage(this, state); + imageCustomPermissionPage = new ImageCustomPermissionPage(this, state); addPage(imageUploadPage); addPage(imageMetaDataPage); addPage(imageCustomPermissionPage); @@ -59,54 +64,60 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { // we can here do all the sanity checks on the fields of UploadWizardState // and react accordingly. // check state - return isStateValid(); - } - - @Override - public void performFinish() { - // push image base to satellite - boolean pushedBase = ThriftActions.updateImageBase(JOptionPane.getFrameForComponent(this), - uploadWizardState.uuid, imageBaseWriteFromState()); - if (!pushedBase) { - Gui.showMessageBox(this, "Konnte die Metadaten des Images nicht auf dem Satelliten speichern!", + if (!isStateValid()) { + // TODO: Show what went wrong + Gui.showMessageBox(this, "Ein interner Fehler ist aufgetreten.\n\nDetails in der Logdatei.", MessageType.ERROR, null, null); - return; + return false; } - // push permissions to satellite if we have custom permissions - boolean pushedPerms = false; - if (uploadWizardState.permissionMap != null) { - pushedPerms = ThriftActions.writeImagePermissions(JOptionPane.getFrameForComponent(this), - uploadWizardState.uuid, uploadWizardState.permissionMap); - if (!pushedPerms) { - Gui.showMessageBox(this, "Konnte die Berechtigungen nicht auf dem Satelliten speichern!", - MessageType.ERROR, null, null); - ThriftActions.deleteImageBase(JOptionPane.getFrameForComponent(this), uploadWizardState.uuid, - null); - return; + // push image base to satellite + if (!baseWritten) { + try { + ThriftActions.updateImageBase(state.uuid, imageBaseWriteFromState()); + } catch (TException e) { + ThriftError.showMessage(null, LOGGER, e, + "Konnte die Metadaten des Images nicht auf dem Satelliten speichern!"); + return false; } + baseWritten = true; } - // push version metadata to satellite - boolean pushedVersion = ThriftActions.updateImageVersion(JOptionPane.getFrameForComponent(this), - uploadWizardState.transferInformation.getToken(), - new ImageVersionWrite(uploadWizardState.isRestricted)); - if (!pushedVersion) { - Gui.showMessageBox(this, "Konnte die Metadaten der Version nicht auf dem Satelliten speichern!", - MessageType.ERROR, null, null); - ThriftActions.deleteImageBase(JOptionPane.getFrameForComponent(this), uploadWizardState.uuid, - null); - return; + // push permissions to satellite if we have custom permissions + if (!permissionsWritten) { + if (state.permissionMap != null && !state.permissionMap.isEmpty()) { + try { + ThriftActions.writeImagePermissions(state.uuid, state.permissionMap); + } catch (TException e) { + Gui.showMessageBox(this, "Konnte die Berechtigungen nicht auf dem Satelliten speichern!", + MessageType.ERROR, null, null); + ThriftActions.deleteImageBase(JOptionPane.getFrameForComponent(this), state.uuid, null); + return false; + } + } + permissionsWritten = true; } - // check upload state - String successMessage = "Image erfolgreich erstellt!"; - if (!uploadWizardState.uploadTask.isComplete()) { - // still running and all of the above worked, so success message - if (!uploadWizardState.uploadTask.isCanceled()) - successMessage += " Der Upload läuft im Hintergrund... Verlassen Sie das Programm nicht solange es läuft."; - } else { - successMessage += " Der Upload ist fertig. Sie können nun eine Veranstaltung anlegen."; - } - Gui.showMessageBox(JOptionPane.getFrameForComponent(this), successMessage, MessageType.INFO, null, null); + state.upload.startUpload(new GotUploadTokenCallback() { + @Override + public void fire() { + // push version data + try { + ThriftActions.updateImageVersion(state.upload.getToken(), new ImageVersionWrite( + state.isRestricted)); + } catch (TException e) { + if (state.isRestricted) { + Gui.showMessageBox(null, "Unerwarteter Fehler beim Setzen der Option" + + " 'Enthält lizenzpflichtige Software' für diese Virtuelle Maschine.", + MessageType.WARNING, LOGGER, e); + } + } + } + }); + return true; + } + + @Override + public WizardPage performFinish() { + return new ImageUploadSummaryPage(this, state, true); } /** @@ -117,53 +128,53 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { */ private boolean isStateValid() { // debug purposes - if (uploadWizardState.name == null || uploadWizardState.name.isEmpty()) { + if (state.name == null || state.name.isEmpty()) { LOGGER.error("No name set in state!"); return false; } - if (uploadWizardState.description == null || uploadWizardState.description.isEmpty()) { + if (state.description == null || state.description.isEmpty()) { LOGGER.error("No description set in state!"); return false; } - if (uploadWizardState.descriptionFile == null) { + if (state.descriptionFile == null) { LOGGER.error("No description file set in state!"); return false; } else { - if (!uploadWizardState.descriptionFile.canRead()) { - LOGGER.error(uploadWizardState.descriptionFile.getAbsolutePath() + " cannot be read!"); + if (!state.descriptionFile.canRead()) { + LOGGER.error(state.descriptionFile.getAbsolutePath() + " cannot be read!"); return false; } } - if (uploadWizardState.diskFile == null) { + if (state.diskFile == null) { LOGGER.error("No disk file set in state!"); return false; } else { - if (!uploadWizardState.diskFile.canRead()) { - LOGGER.error(uploadWizardState.diskFile.getAbsolutePath() + " cannot be read!"); + if (!state.diskFile.canRead()) { + LOGGER.error(state.diskFile.getAbsolutePath() + " cannot be read!"); return false; } } - if (uploadWizardState.selectedOs == null) { + if (state.selectedOs == null) { LOGGER.error("No OS set in state!"); return false; } - if (!uploadWizardState.selectedOs.isSetOsId()) { - LOGGER.error("OS has no id: " + uploadWizardState.selectedOs.toString()); + if (!state.selectedOs.isSetOsId()) { + LOGGER.error("OS has no id: " + state.selectedOs.toString()); return false; } - if (uploadWizardState.meta == null) { + if (state.meta == null) { LOGGER.error("No vm meta data set in state!"); return false; } - if (uploadWizardState.defaultPermissions == null) { + if (state.defaultPermissions == null) { LOGGER.error("No permissions set in state!"); return false; } - if (uploadWizardState.shareMode == null) { + if (state.shareMode == null) { LOGGER.error("No share mode set in state!"); return false; } - if (uploadWizardState.uuid == null) { + if (state.uuid == null) { LOGGER.error("No uuid set in state!"); return false; } @@ -172,15 +183,14 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { private ImageBaseWrite imageBaseWriteFromState() { // build imageBaseWrite - return new ImageBaseWrite(uploadWizardState.name, uploadWizardState.description, - uploadWizardState.selectedOs.getOsId(), uploadWizardState.meta.getVirtualizer().getVirtId(), - uploadWizardState.isTemplate, uploadWizardState.defaultPermissions, - uploadWizardState.shareMode); + return new ImageBaseWrite(state.name, state.description, state.selectedOs.getOsId(), + state.meta.getVirtualizer().getVirtId(), state.isTemplate, state.defaultPermissions, + state.shareMode); } @Override protected boolean onCancelRequest() { - if (uploadWizardState.uuid == null) + if (state.uuid == null) return true; boolean confirmed = Gui.showMessageBox(this, "Möchten Sie den Vorgang wirklich abbrechen?", MessageType.QUESTION_YESNO, null, null); @@ -188,18 +198,15 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { QuickTimer.scheduleOnce(new Task() { @Override public void fire() { + if (state.upload != null) { + state.upload.cancelError(); + } + // As we're creating a new VM, delete base image aswell try { - ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), - uploadWizardState.uuid); + ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), state.uuid); } catch (TException e) { LOGGER.debug("Error canceling upload on sat: ", e); } - if (uploadWizardState.uploadTask != null) { - uploadWizardState.uploadTask.cancel(); - } - if (uploadWizardState.hashGen != null) { - uploadWizardState.hashGen.cancel(); - } } }); } @@ -208,7 +215,7 @@ public class ImageCreationWizard extends Wizard implements UiFeedback { @Override public boolean wantConfirmQuit() { - return uploadWizardState.uuid != null; + return state.uuid != null; } @Override diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageUpdateWizard.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageUpdateWizard.java index 54b3af39..dc526f12 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageUpdateWizard.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/ImageUpdateWizard.java @@ -2,49 +2,95 @@ package org.openslx.dozmod.gui.wizard; import java.awt.Window; -import javax.swing.JOptionPane; - import org.apache.log4j.Logger; +import org.apache.thrift.TException; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.bwlp.thrift.iface.ImageVersionWrite; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.gui.helper.UiFeedback; import org.openslx.dozmod.gui.wizard.page.ImageUploadPage; +import org.openslx.dozmod.gui.wizard.page.ImageUploadSummaryPage; import org.openslx.dozmod.state.UploadWizardState; import org.openslx.dozmod.thrift.ThriftActions; +import org.openslx.dozmod.thrift.ThriftError; +import org.openslx.dozmod.thrift.UploadInitiator.GotUploadTokenCallback; +import org.openslx.util.QuickTimer; +import org.openslx.util.QuickTimer.Task; @SuppressWarnings("serial") public class ImageUpdateWizard extends Wizard implements UiFeedback { private final static Logger LOGGER = Logger.getLogger(ImageCreationWizard.class); - private final UploadWizardState uploadWizardState = new UploadWizardState(); - + private final UploadWizardState state = new UploadWizardState(); + protected ImageUploadPage imageUploadPage; + public ImageUpdateWizard(final Window parent, final ImageDetailsRead image) { super(parent); - - imageUploadPage = new ImageUploadPage(this, uploadWizardState, image); + + imageUploadPage = new ImageUploadPage(this, state, image); addPage(imageUploadPage); } @Override - public void performFinish() { - boolean ret = ThriftActions.updateImageVersion(JOptionPane.getFrameForComponent(this), - uploadWizardState.transferInformation.getToken(), - new ImageVersionWrite(uploadWizardState.isRestricted)); - if (!ret) { - Gui.showMessageBox(this, "Konnte Metadaten der Version nicht am Satelliten speichern!", MessageType.INFO, null, null); + protected boolean wantFinish() { + try { + ThriftActions.updateImageVersion(state.upload.getToken(), new ImageVersionWrite( + state.isRestricted)); + } catch (TException e) { + ThriftError.showMessage(null, LOGGER, e, "Serverseitiger Fehler beim Speichern der Image-Daten."); + return false; + } + state.upload.startUpload(new GotUploadTokenCallback() { + @Override + public void fire() { + // push version data + try { + ThriftActions.updateImageVersion(state.upload.getToken(), new ImageVersionWrite( + state.isRestricted)); + } catch (TException e) { + if (state.isRestricted) { + Gui.showMessageBox(null, "Unerwarteter Fehler beim Setzen der Option" + + " 'Enthält lizenzpflichtige Software' für diese Virtuelle Maschine.", + MessageType.WARNING, LOGGER, e); + } + } + } + }); + return true; + } + + @Override + public WizardPage performFinish() { + return new ImageUploadSummaryPage(this, state, false); + } + + @Override + protected boolean onCancelRequest() { + if (state.uuid == null) + return true; + boolean confirmed = Gui.showMessageBox(this, "Möchten Sie den Vorgang wirklich abbrechen?", + MessageType.QUESTION_YESNO, null, null); + if (confirmed && state.upload != null) { + QuickTimer.scheduleOnce(new Task() { + @Override + public void fire() { + state.upload.cancelError(); + } + }); } + return confirmed; } + @Override public boolean wantConfirmQuit() { - return uploadWizardState.transferInformation != null; + return state.upload != null; } @Override public void escapePressed() { - dispose(); + doCancel(); } @Override diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/Wizard.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/Wizard.java index 17aa9617..ebf82642 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/Wizard.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/Wizard.java @@ -22,7 +22,6 @@ import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.SwingConstants; -import org.apache.log4j.Logger; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.control.QLabel; import org.openslx.dozmod.gui.helper.GridManager; @@ -30,11 +29,11 @@ import org.openslx.dozmod.gui.helper.GridManager; @SuppressWarnings("serial") public abstract class Wizard extends JDialog { - private static final Logger LOGGER = Logger.getLogger(Wizard.class); - private final QLabel titleLabel; private final QLabel messageLabel; private final List<WizardPage> pages = new ArrayList<>(); + private WizardPage postFinishPage = null; + private boolean isPostFinish = false; private final JPanel contentPanel; private int currentPage = -1; private boolean needsLayout = true; @@ -90,7 +89,7 @@ public abstract class Wizard extends JDialog { contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS)); contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); add(contentPanel, BorderLayout.CENTER); - + // Scale window with font size setPreferredSize(Gui.getScaledDimension(550, 420)); setResizable(false); @@ -147,22 +146,21 @@ public abstract class Wizard extends JDialog { private void showPage(int index) { if (currentPage != -1) { - WizardPage old = pages.get(currentPage); + WizardPage old = getPage(currentPage); old.onPageLeave(); old.setVisible(false); } - WizardPage page = pages.get(index); + WizardPage page = getPage(index); page.onPageEnter(); page.setVisible(true); currentPage = index; updateHeader(page); updateButtons(page); validate(); - } void updateHeader(WizardPage page) { - if (currentPage == -1 || pages.get(currentPage) != page) + if (!isPostFinish && (currentPage == -1 || getPage(currentPage) != page)) return; String pageTitle = page.getTitle(); String pageDesc = page.getMessage(); @@ -172,7 +170,7 @@ public abstract class Wizard extends JDialog { pageDesc = ""; titleLabel.setText(pageTitle); messageLabel.setText(pageDesc); - messageLabel.setIcon(page.getMessageIcon()); + messageLabel.setIcon(page.getMessageIcon()); // TODO: Scale messageLabel.setForeground(page.getMessageColor()); messageLabel.validate(); setTitle(getWindowTitle() + " - " + pageTitle); @@ -188,7 +186,15 @@ public abstract class Wizard extends JDialog { pages.add(page); } + protected void addSummaryPage(WizardPage page) { + postFinishPage = page; + } + void updateButtons(WizardPage page) { + if (isPostFinish) { + btnFinish.setEnabled(postFinishPage.isComplete()); + return; + } // State of finish button boolean canFinish = true; for (WizardPage p : pages) { @@ -199,24 +205,28 @@ public abstract class Wizard extends JDialog { } btnFinish.setEnabled(canFinish); // State of next button - if (currentPage != -1 && pages.get(currentPage) == page) { + if (currentPage != -1 && getPage(currentPage) == page) { btnNext.setEnabled(currentPage + 1 < pages.size() && page.isComplete()); - btnPrev.setEnabled(currentPage > 0 && pages.get(currentPage - 1).canComeBack); + btnPrev.setEnabled(currentPage > 0 && getPage(currentPage - 1).canComeBack); } } + protected final boolean isSummaryPage() { + return isPostFinish; + } + protected final void doNext() { - if (!btnNext.isEnabled()) + if (isPostFinish || !btnNext.isEnabled()) return; if (currentPage + 1 < pages.size()) { - if (!pages.get(currentPage).wantNextOrFinish()) + if (!getPage(currentPage).wantNextOrFinish()) return; // Page canceled the operation showPage(currentPage + 1); } } protected final void doPrevious() { - if (!btnPrev.isEnabled()) + if (isPostFinish || !btnPrev.isEnabled()) return; if (currentPage > 0) { showPage(currentPage - 1); @@ -224,26 +234,54 @@ public abstract class Wizard extends JDialog { } protected final void doCancel() { - if (onCancelRequest()) + if (onCancelRequest()) { + if (currentPage != -1) { + getPage(currentPage).onPageLeave(); + } + if (isPostFinish) { + postFinishPage.onPageLeave(); + } dispose(); + } } protected final void doFinish() { + if (isPostFinish) { + postFinishPage.onPageLeave(); + dispose(); + return; + } if (!btnFinish.isEnabled()) return; if (currentPage != -1) { - if (!pages.get(currentPage).wantNextOrFinish()) + if (!getPage(currentPage).wantNextOrFinish()) return; } if (wantFinish()) { if (currentPage != -1) { - pages.get(currentPage).onPageLeave(); + getPage(currentPage).onPageLeave(); + } + postFinishPage = performFinish(); + if (postFinishPage == null) { + dispose(); + } else { + isPostFinish = true; + btnPrev.setVisible(false); + btnNext.setVisible(false); + btnFinish.setText("Schließen"); + postFinishPage.setVisible(false); + contentPanel.add(postFinishPage); + showPage(-1); } - performFinish(); - dispose(); } } + private WizardPage getPage(int index) { + if (isPostFinish && index == -1) + return postFinishPage; + return pages.get(index); + } + /* * Callback to wizard implementation */ @@ -273,9 +311,15 @@ public abstract class Wizard extends JDialog { * Called so the wizard can perform finishing steps. This is called after * wantFinish() returned true and onPageLeave was called on the currently * visible wizard step. + * If a wizard page is returned, it will serve as a summary page shown to + * the user within the wizard frame. The prev and next buttons will + * disappear, and the finish button will change to a close button. + * + * @return A wizard page that serves as a summary page, or null if the + * wizard should just close */ - protected void performFinish() { - // void + protected WizardPage performFinish() { + return null; } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/WizardPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/WizardPage.java index ec77de90..9c5044f4 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/WizardPage.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/WizardPage.java @@ -101,6 +101,8 @@ public abstract class WizardPage extends JPanel { */ protected void setPageComplete(boolean b) { + if (isComplete == b) + return; isComplete = b; if (wizard != null) wizard.updateButtons(this); diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageUploadSummaryPageLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageUploadSummaryPageLayout.java new file mode 100644 index 00000000..930d5e16 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageUploadSummaryPageLayout.java @@ -0,0 +1,46 @@ +package org.openslx.dozmod.gui.wizard.layout; + +import javax.swing.Box; +import javax.swing.JCheckBox; +import javax.swing.JLabel; + +import org.openslx.dozmod.gui.control.BlockProgressBar; +import org.openslx.dozmod.gui.helper.GridManager; +import org.openslx.dozmod.gui.wizard.Wizard; +import org.openslx.dozmod.gui.wizard.WizardPage; + +@SuppressWarnings("serial") +public class ImageUploadSummaryPageLayout extends WizardPage { + + protected final BlockProgressBar progressBar; + protected final JCheckBox chkCreateLecture; + protected final JLabel infoText; + + public ImageUploadSummaryPageLayout(Wizard wizard, boolean allowCreateLecture) { + super(wizard, "Fertig!"); + setDescription("Assistent abgeschlossen"); + GridManager grid = new GridManager(this, 1); + + progressBar = new BlockProgressBar(null); + grid.add(progressBar).expand(true, false).fill(true, false); + grid.nextRow(); + + grid.add(Box.createVerticalStrut(16)); + grid.nextRow(); + + infoText = new JLabel(); + grid.add(infoText).expand(true, false).fill(true, false); + grid.nextRow(); + + grid.add(Box.createVerticalGlue()).expand(true, true); + grid.nextRow(); + + chkCreateLecture = new JCheckBox("Veranstaltung zu dieser VM erstellen"); + chkCreateLecture.setVisible(allowCreateLecture); + grid.add(chkCreateLecture); + grid.nextRow(); + + grid.finish(false); + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureRestrictionPageLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureRestrictionPageLayout.java new file mode 100644 index 00000000..431d20e3 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureRestrictionPageLayout.java @@ -0,0 +1,15 @@ +package org.openslx.dozmod.gui.wizard.layout; + +import org.openslx.dozmod.gui.wizard.Wizard; +import org.openslx.dozmod.gui.wizard.WizardPage; + +@SuppressWarnings("serial") +public class LectureRestrictionPageLayout extends WizardPage { + + public LectureRestrictionPageLayout(Wizard wizard) { + super(wizard, "Beschränkungen"); + + + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadPage.java index a32656b4..d6f8c6aa 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadPage.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadPage.java @@ -17,14 +17,15 @@ import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.dozmod.Config; 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.wizard.Wizard; import org.openslx.dozmod.gui.wizard.layout.ImageUploadPageLayout; import org.openslx.dozmod.state.UploadWizardState; import org.openslx.dozmod.thrift.ThriftActions; -import org.openslx.dozmod.thrift.ThriftActions.InitUploadStatus; +import org.openslx.dozmod.thrift.ThriftError; +import org.openslx.dozmod.thrift.UploadInitiator; +import org.openslx.dozmod.thrift.WrappedException; import org.openslx.dozmod.thrift.cache.MetaDataCache; import org.openslx.util.vm.VmMetaData.HardDisk; import org.openslx.util.vm.VmwareMetaData; @@ -183,32 +184,22 @@ public class ImageUploadPage extends ImageUploadPageLayout { } else { state.uuid = existingImage.getImageBaseId(); } - // -- request upload for that uuid -- - if (state.transferInformation == null) { - state.transferInformation = ThriftActions.requestVersionUpload( - JOptionPane.getFrameForComponent(this), state.uuid, state.diskFile.length(), null, - state.meta.getFilteredDefinition()); - if (state.transferInformation == null) + // Create upload initiator that will manage requesting a token, hashing the file, connecting for upload... + if (state.upload == null) { + try { + state.upload = new UploadInitiator(state.uuid, state.diskFile, + state.meta.getFilteredDefinition()); + } catch (WrappedException e) { + ThriftError.showMessage(this, LOGGER, e.exception, e.displayMessage); + return false; + } catch (IOException e) { + Gui.showMessageBox(this, "Upload-Initialisierung fehlgeschlagen", MessageType.ERROR, LOGGER, + e); return false; - } - // -- start uploading our diskFile with the transfer information we just received -- - if (state.uploadTask == null) { - InitUploadStatus ret = ThriftActions.initUpload(JOptionPane.getFrameForComponent(this), - state.transferInformation, state.diskFile); - if (ret != null) { - if (ret.task == null) { - return false; - } else { - state.uploadTask = ret.task; - } - if (ret.hasher == null) { - return false; - } else { - state.hashGen = ret.hasher; - } } } - MainWindow.addUpload(state); + // Start the hash check now + state.upload.startHashing(); return true; } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadSummaryPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadSummaryPage.java new file mode 100644 index 00000000..8c7d653d --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageUploadSummaryPage.java @@ -0,0 +1,138 @@ +package org.openslx.dozmod.gui.wizard.page; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.TransferState; +import org.openslx.dozmod.filetransfer.TransferEvent; +import org.openslx.dozmod.filetransfer.TransferEventListener; +import org.openslx.dozmod.gui.Gui; +import org.openslx.dozmod.gui.MainWindow; +import org.openslx.dozmod.gui.wizard.Wizard; +import org.openslx.dozmod.gui.wizard.layout.ImageUploadSummaryPageLayout; +import org.openslx.dozmod.state.UploadWizardState; +import org.openslx.dozmod.thrift.UploadInitiator.UploadInitState; +import org.openslx.util.QuickTimer; + +@SuppressWarnings("serial") +public class ImageUploadSummaryPage extends ImageUploadSummaryPageLayout { + + private static final Logger LOGGER = Logger.getLogger(ImageUploadSummaryPage.class); + + private boolean pageIsVisible = false; + private boolean uploadListenerAdded = false; + private final UploadWizardState state; + + private final TransferEventListener uploadListener = new TransferEventListener() { + @Override + public void update(TransferEvent event) { + LOGGER.debug("update transfer event"); + if (!pageIsVisible) + return; + if (event.progress != null) { + progressBar.setStatus(event.progress); + if (event.state == TransferState.FINISHED) { + updateInfoText(null); + state.upload.getUploadTask().removeListener(uploadListener); + } + } + } + }; + + private final QuickTimer.Task updateHelpText = new QuickTimer.Task() { + private UploadInitState lastInitState = null; + + @Override + public void fire() { + LOGGER.debug("update help text"); + if (!pageIsVisible) { + this.cancel(); + return; + } + if (!uploadListenerAdded && state.upload.getUploadTask() != null) { + uploadListenerAdded = true; + state.upload.getUploadTask().addListener(uploadListener); + } + UploadInitState initState = state.upload.getState(); + if (initState != lastInitState) { + lastInitState = initState; + updateInfoText(initState); + } + if (lastInitState == UploadInitState.UPLOAD_STARTED || lastInitState == UploadInitState.ERROR) { + this.cancel(); + } + } + }; + + private void updateInfoText(final UploadInitState initState) { + String text; + if (state.upload.getUploadTask() != null && state.upload.getUploadTask().isComplete()) { + text = "Die Virtuelle Maschine wurde erfolgreich auf den Server hochgeladen."; + } else if (initState == null) { + return; + } else { + switch (initState) { + case ERROR: + text = "Die Initialisierung des Uploads auf den Server ist fehlgeschlagen." + + " Sie können versuchen, den Vorgang erneut zu starten." + + " Falls das Problem weiterhin besteht," + + " kontaktieren Sie den bwLehrpool-Support Ihrer Einrichtung."; + if (state.upload.getErrorMessage() != null) { + text += "<br><br>Weitere Informationen:<br>" + state.upload.getErrorMessage(); + } + break; + case REQUESTING: + text = "Der Upload-Vorgang wird mit dem Server ausgehandelt..."; + break; + case UPLOAD_STARTED: + text = "Der Upload Ihrer Virtuellen Maschine wurde gestartet." + + " Wenn Sie möchten, können Sie diesen Assistenten schließen," + + " und die Anwendung weiterverwenden." + + " Die Übertragung läuft dabei im Hintergrund weiter."; + break; + case UPLOAD_STARTING: + text = "Die Verbindung zur Übertragung der VM wird aufgebaut..."; + break; + case WAITING_FOR_SLOT: + text = "Der Server ist zur Zeit überlastet, da zu viele Uploads gleichzeitig laufen." + + " Es wird gewartet, bis der Server wieder Kapazitäten frei hat." + + " Bitte schließen Sie dieses Fenster nicht."; + break; + default: + return; + } + } + final String t = text; + Gui.asyncExec(new Runnable() { + @Override + public void run() { + infoText.setText("<html><body style ='width:100%'>" + t + "</body></html>"); + setPageComplete(initState == UploadInitState.UPLOAD_STARTED); + } + }); + } + + public ImageUploadSummaryPage(Wizard wizard, UploadWizardState state, boolean allowCreateLecture) { + super(wizard, allowCreateLecture); + this.state = state; + setPageComplete(false); + } + + @Override + protected void onPageEnter() { + if (pageIsVisible) + return; + pageIsVisible = true; + QuickTimer.scheduleAtFixedDelay(updateHelpText, 1, 3000); + } + + @Override + protected void onPageLeave() { + if (!pageIsVisible) + return; + pageIsVisible = false; + state.upload.getUploadTask().removeListener(uploadListener); + if (!state.upload.getUploadTask().isCanceled()) { + MainWindow.addUpload(state); + } + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/state/UploadWizardState.java b/dozentenmodul/src/main/java/org/openslx/dozmod/state/UploadWizardState.java index c340f562..3cb6f2f3 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/state/UploadWizardState.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/state/UploadWizardState.java @@ -6,10 +6,8 @@ import java.util.Map; import org.openslx.bwlp.thrift.iface.ImagePermissions; import org.openslx.bwlp.thrift.iface.OperatingSystem; import org.openslx.bwlp.thrift.iface.ShareMode; -import org.openslx.bwlp.thrift.iface.TransferInformation; -import org.openslx.dozmod.filetransfer.AsyncHashGenerator; -import org.openslx.dozmod.filetransfer.UploadTask; import org.openslx.dozmod.thrift.Session; +import org.openslx.dozmod.thrift.UploadInitiator; import org.openslx.util.vm.VmMetaData; public class UploadWizardState { @@ -42,20 +40,12 @@ public class UploadWizardState { // -- Objects returned by thrift calls -- // UUID given returned by the satellite after creating the image public String uuid = null; - // transfer information for upload received if the upload request was granted - public TransferInformation transferInformation = null; - /** - * The upload task representing this new VM - */ - public UploadTask uploadTask = null; + + public UploadInitiator upload = null; /** * Meta data (description file content) of this VM */ public VmMetaData meta = null; - /** - * Hash calculator - */ - public AsyncHashGenerator hashGen; } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/GuiErrorCallback.java b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/GuiErrorCallback.java index 11c3c3da..26e11504 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/GuiErrorCallback.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/GuiErrorCallback.java @@ -25,23 +25,26 @@ public class GuiErrorCallback implements ErrorCallback { @Override public boolean thriftError(int failCount, final String method, final Throwable t) { // if it's not a transport exception, do not retry - if (!(t instanceof TTransportException)) + if (t != null && !(t instanceof TTransportException)) return false; - final TTransportException tex = (TTransportException) t; // if it's the first fail, retry immediately if (failCount == 1) return true; // Some methods are non-critical, so don't show a pop-up if (ThriftError.failSilently(method)) - return false; + return failCount == 2; // As it's silent, give it a second try... // Otherwise, ask user if we should retry + final TTransportException tex = (TTransportException) t; return Gui.syncExec(new GuiCallable<Boolean>() { @Override public Boolean run() { + String errMsg = null; + if (tex != null) { + errMsg = " (Fehler " + tex.getType() + ")"; + } return Gui.showMessageBox(parent, "Die Kommunikation mit " + serverString + " ist" - + " gestört. Der Aufruf der Funktion " + method + " ist fehlgeschlagen (Fehler " - + tex.getType() + ").\n\n" + "Möchten Sie den Aufruf wiederholen?", - MessageType.ERROR_RETRY, LOGGER, t); + + " gestört. Der Aufruf der Funktion " + method + " ist fehlgeschlagen" + errMsg + + ".\n\n" + "Möchten Sie den Aufruf wiederholen?", MessageType.ERROR_RETRY, LOGGER, t); } }); } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java index a1711990..d22bf39c 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java @@ -5,8 +5,6 @@ import java.awt.Window; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -28,6 +26,7 @@ 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; @@ -36,11 +35,9 @@ 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.AsyncHashGenerator; import org.openslx.dozmod.filetransfer.DownloadTask; import org.openslx.dozmod.filetransfer.TransferEvent; import org.openslx.dozmod.filetransfer.TransferEventListener; -import org.openslx.dozmod.filetransfer.UploadTask; import org.openslx.dozmod.gui.GraphicalCertHandler; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.MainWindow; @@ -58,7 +55,6 @@ 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.Util; import org.openslx.util.vm.DiskImage; import org.openslx.util.vm.DiskImage.UnknownImageFormatException; @@ -66,7 +62,6 @@ public class ThriftActions { private static final Logger LOGGER = Logger.getLogger(ThriftActions.class); private static final long SIZE_CHECK_EXTRA_DL = 50l * 1024l * 1024l; - private static final long SIZE_CHECK_EXTRA_UL = 150l * 1024l * 1024l; /* ******************************************************************************* * @@ -74,8 +69,7 @@ public class ThriftActions { * * Login methods * - * ************************************************************************** - * **** + * ******************************************************************************* */ /** * @param window the parentWindow @@ -108,7 +102,9 @@ public class ThriftActions { } else { if (data.satellites.isEmpty()) { Gui.asyncMessageBox("Login erfolgreich, aber es wurde kein Satellit-Server gefunden.\n" - + " Bitte geben Sie die Adresse Ihres Satelliten an.", MessageType.ERROR, LOGGER, null);} + + " Bitte geben Sie die Adresse Ihres Satelliten an.", MessageType.ERROR, LOGGER, + null); + } sat = SatelliteListWindow.open(window, data.satellites); } @@ -137,7 +133,7 @@ public class ThriftActions { 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); + MessageType.ERROR, null, null); } return false; } @@ -167,7 +163,7 @@ public class ThriftActions { Gui.asyncMessageBox( "Authentifizierung erfolgreich, der Satellit verweigert jedoch die Verbindung.\n\n" + "Grund: " + e.number.toString() + " (" + e.message + ")", - MessageType.ERROR, null, null); + MessageType.ERROR, null, null); } return false; } catch (TInvocationException e) { @@ -213,8 +209,7 @@ public class ThriftActions { * * Creates a base image with the given name * - * ************************************************************************** - * **** + * ******************************************************************************* */ /** * GUI-BLOCKING Creates the image with the given name. Returns the uuid @@ -240,39 +235,34 @@ public class ThriftActions { * 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 + * @throws TException + * @throws TInvocationException + * @throws TNotFoundException + * @throws TAuthorizationException */ - public static boolean 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"); - return false; - } - return true; + 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 frame to show user feedback on * @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 boolean 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"); - return false; - } - return true; + public static void writeImagePermissions(final String imageBaseId, + final Map<String, ImagePermissions> permissions) throws TAuthorizationException, + TNotFoundException, TInvocationException, TException { + ThriftManager.getSatClient().writeImagePermissions(Session.getSatelliteToken(), imageBaseId, + permissions); } /* ******************************************************************************* @@ -283,124 +273,23 @@ public class ThriftActions { * 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) { - try { - if (ThriftManager.getSatClient().getStatus().getAvailableStorageBytes() < fileSize - + SIZE_CHECK_EXTRA_UL) { - Gui.showMessageBox( - frame, - "Nicht genügend Speicherplatz Satelliten. Löschen Sie nicht verwendete Imageversionen oder kontaktieren sie den Administrator.", - MessageType.ERROR, LOGGER, null); - return null; - } - } catch (TException e1) { - ThriftError.showMessage(frame, LOGGER, e1, "Konnte Status des Satelliten nicht abfragen!"); - return null; - } - 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 InitUploadStatus 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 new InitUploadStatus(uploadTask, hashGen); - } - - /** - * GUI-BLOCKING Gives user feedback TODO + * GUI-BLOCKING * * @param frame * @param transferInformation * @param versionInfo + * @throws TException + * @throws TInvocationException + * @throws TNotFoundException + * @throws TAuthorizationException */ - public static boolean 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 false; - } - return true; + public static void updateImageVersion(final String versionId, final ImageVersionWrite versionInfo) + throws TAuthorizationException, TNotFoundException, TInvocationException, TException { + ThriftManager.getSatClient().updateImageVersion(Session.getSatelliteToken(), versionId, versionInfo); } /* ******************************************************************************* @@ -411,8 +300,7 @@ public class ThriftActions { * 'DownloadCallback' and the actual static method 'initDownload' to start * the download. * - * ************************************************************************** - * **** + * ******************************************************************************* */ /** * The callback interface to inform the GUI about the status of the @@ -548,7 +436,7 @@ public class ThriftActions { 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); + MessageType.WARNING, LOGGER, e); } } }); @@ -1140,18 +1028,4 @@ public class ThriftActions { return Gui.showMessageBox(frame, message, MessageType.QUESTION_YESNO, null, null); } - /** - * Wrapper class for UploadTask & AsyncHashGenerator - * - */ - public static class InitUploadStatus { - public final UploadTask task; - public final AsyncHashGenerator hasher; - - public InitUploadStatus(final UploadTask task, final AsyncHashGenerator hasher) { - this.task = task; - this.hasher = hasher; - } - } - } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/UploadInitiator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/UploadInitiator.java new file mode 100644 index 00000000..b176a5cb --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/UploadInitiator.java @@ -0,0 +1,241 @@ +package org.openslx.dozmod.thrift; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; +import org.apache.thrift.TException; +import org.openslx.bwlp.thrift.iface.SatelliteStatus; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; +import org.openslx.bwlp.thrift.iface.TTransferRejectedException; +import org.openslx.bwlp.thrift.iface.TransferInformation; +import org.openslx.dozmod.filetransfer.AsyncHashGenerator; +import org.openslx.dozmod.filetransfer.UploadTask; +import org.openslx.thrifthelper.ThriftManager; +import org.openslx.util.QuickTimer; + +public class UploadInitiator { + + private static final Logger LOGGER = Logger.getLogger(UploadInitiator.class); + + private static final long SIZE_CHECK_EXTRA_UL = 150l * 1024l * 1024l; + + public enum UploadInitState { + IDLE, + REQUESTING, + WAITING_FOR_SLOT, + UPLOAD_STARTING, + UPLOAD_STARTED, + ERROR + } + + private final File diskFile; + private final long fileSize; + private final String imageBaseId; + private final ByteBuffer machineDescription; + private final AsyncHashGenerator hashGen; + private TransferInformation transferInformation = null; + private UploadTask uploadTask; + private UploadInitState initState = UploadInitState.IDLE; + private String errorMessage = null; + + /** + * 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 imageBaseId uuid of the image to upload a version of + * @param diskFile the file to upload + * @param machineDescription + * @throws TException + * @throws IOException + */ + public UploadInitiator(final String imageBaseId, final File diskFile, final ByteBuffer machineDescription) + throws WrappedException, IOException { + if (!diskFile.canRead()) + throw new FileNotFoundException(diskFile.getName()); + this.fileSize = diskFile.length(); + SatelliteStatus status; + try { + status = ThriftManager.getSatClient().getStatus(); + } catch (TException e1) { + throw new WrappedException(e1, "Konnte Status des Satelliten nicht abfragen!"); + } + if (status.getAvailableStorageBytes() != -1 + && status.getAvailableStorageBytes() < fileSize + SIZE_CHECK_EXTRA_UL) { + throw new IOException("Nicht genügend Speicherplatz Satelliten.\n" + + "Löschen Sie nicht verwendete Imageversionen oder kontaktieren sie den Administrator."); + } + this.diskFile = diskFile; + AsyncHashGenerator hg; + try { + hg = new AsyncHashGenerator(diskFile); + } catch (NoSuchAlgorithmException e) { + LOGGER.warn("Cannot instantiate hash generator: No error correction available!"); + hg = null; + } + this.hashGen = hg; + this.machineDescription = machineDescription; + this.imageBaseId = imageBaseId; + } + + public synchronized void startHashing() throws IllegalThreadStateException { + if (hashGen == null) + return; + hashGen.start(); + } + + public interface GotUploadTokenCallback { + public void fire(); + } + GotUploadTokenCallback gotTokenCallback = null; + + public synchronized void startUpload(GotUploadTokenCallback gotTokenCallback) { + if (initState != UploadInitState.IDLE) + throw new IllegalStateException("Upload already started"); + this.gotTokenCallback = gotTokenCallback; + initState = UploadInitState.REQUESTING; + QuickTimer.scheduleAtFixedRate(startUploadInternal, 1, TimeUnit.SECONDS.toMillis(15)); + } + + private QuickTimer.Task startUploadInternal = new QuickTimer.Task() { + @Override + public void fire() { + LOGGER.debug("start upload internal"); + try { + transferInformation = ThriftManager.getSatClient().requestImageVersionUpload( + Session.getSatelliteToken(), imageBaseId, fileSize, null, // TODO remove deprecated parameter + machineDescription); + } catch (TAuthorizationException e) { + errorMessage = "Upload vom Server verweigert"; + cancelError(); + this.cancel(); + return; + } catch (TTransferRejectedException e) { + if (e.message != null && e.message.startsWith("Server busy")) { + initState = UploadInitState.WAITING_FOR_SLOT; + } else { + errorMessage = "Upload-Anfrage gescheitert!"; + cancelError(); + this.cancel(); + } + return; + } catch (TException e) { + errorMessage = "Upload-Anfrage gescheitert!"; + cancelError(); + this.cancel(); + return; + } + // Everything worked out so far + LOGGER.info("Version upload granted, versionId: " + transferInformation.toString()); + if (gotTokenCallback != null) { + gotTokenCallback.fire(); + } + if (hashGen != null) { + hashGen.setUploadToken(transferInformation.token); + } + initState = UploadInitState.UPLOAD_STARTING; + QuickTimer.scheduleAtFixedRate(launchUploadTask, 1, 100); + this.cancel(); + } + }; + + /** + * Prepare the given file for uploading + * + * @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 + */ + private QuickTimer.Task launchUploadTask = new QuickTimer.Task() { + @Override + public void fire() { + LOGGER.debug("launch upload task"); + if (initState != UploadInitState.UPLOAD_STARTING) { + this.cancel(); + return; + } + if (uploadTask == null) { + // do actually start the upload now + LOGGER.debug("Starting upload for: " + diskFile.getName()); + try { + uploadTask = new UploadTask(Session.getSatelliteAddress(), + transferInformation.getPlainPort(), transferInformation.getToken(), diskFile); + } catch (FileNotFoundException e) { + errorMessage = "Kann VM nicht hochladen: Datei nicht gefunden\n\n" + + diskFile.getAbsolutePath(); + cancelError(); + this.cancel(); + return; + } + Thread uploadThread = new Thread(uploadTask); + uploadThread.setDaemon(true); + uploadThread.start(); + } + + if (uploadTask.getFailCount() == 0 && uploadTask.getTransferCount() == 0 + && !uploadTask.isCanceled()) { + // Still initializing, wait... + return; + } + if (!uploadTask.isComplete() && uploadTask.getTransferCount() == 0) { + // Init failed + errorMessage = "Aufbau der Verbindung zum Hochladen fehlgeschlagen"; + cancelError(); + this.cancel(); + return; + } + // Init succeeded + initState = UploadInitState.UPLOAD_STARTED; + this.cancel(); + } + }; + + /** + * Set state to cancelled and clean up + */ + public synchronized void cancelError() { + if (initState == UploadInitState.ERROR) + return; + initState = UploadInitState.ERROR; + if (hashGen != null) { + hashGen.cancel(); + } + if (uploadTask != null) { + uploadTask.cancel(); + } + if (transferInformation != null) { + try { + ThriftManager.getSatClient().cancelUpload(transferInformation.token); + } catch (Exception e) { + } + } + } + + public UploadInitState getState() { + return initState; + } + + public String getErrorMessage() { + return errorMessage; + } + + public AsyncHashGenerator getHasher() { + return hashGen; + } + + public UploadTask getUploadTask() { + return uploadTask; + } + + public String getToken() { + return transferInformation == null ? null : transferInformation.token; + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/WrappedException.java b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/WrappedException.java new file mode 100644 index 00000000..1eb9783a --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/WrappedException.java @@ -0,0 +1,15 @@ +package org.openslx.dozmod.thrift; + +import org.apache.thrift.TException; + + +public class WrappedException extends Exception { + private static final long serialVersionUID = 989657721513312675L; + public final TException exception; + public final String displayMessage; + + public WrappedException(TException e, String msg) { + this.exception = e; + this.displayMessage = msg; + } +} |