From 06da2d026b1af4565be7efb9f9cdc8fe6cb6cc52 Mon Sep 17 00:00:00 2001 From: anhefti Date: Sat, 11 May 2019 16:47:10 +0200 Subject: [PATCH] asynchronous zip end encryption streaming for seb configs download --- .../seb/sebserver/gbl/async/AsyncRunner.java | 5 + .../seb/sebserver/gbl/async/AsyncService.java | 47 +++++ .../gbl/async/AsyncServiceSpringConfig.java | 10 +- .../gbl/model/sebconfig/AttributeType.java | 3 +- .../gui/service/examconfig/InputField.java | 1 + .../impl/SingleSelectionFieldBuilder.java | 132 ++++++++++++++ .../examconfig/impl/TableFieldBuilder.java | 11 -- .../sebserver/gui/widget/ColorSelection.java | 11 ++ .../sebserver/gui/widget/MultiSelection.java | 11 ++ .../gui/widget/MultiSelectionCombo.java | 14 ++ .../seb/sebserver/gui/widget/Selection.java | 8 +- .../sebserver/gui/widget/SingleSelection.java | 6 + .../sebconfig/SebClientConfigService.java | 6 +- .../sebconfig/SebConfigCryptor.java | 12 ++ .../sebconfig/SebConfigEncryptionService.java | 17 +- .../servicelayer/sebconfig/ZipService.java | 19 ++ .../sebconfig/impl/PasswordEncryptor.java | 68 +++++++- .../impl/SebClientConfigServiceImpl.java | 165 +++++++++++------- .../impl/SebConfigEncryptionServiceImpl.java | 55 ++++-- .../sebconfig/impl/ZipServiceImpl.java | 76 ++++++++ .../weblayer/api/ControllerConfig.java | 41 +++++ .../api/SebClientConfigController.java | 29 ++- .../sebconfig/impl/PasswordEncryptorTest.java | 123 +++++++++++++ .../SebConfigEncryptionServiceImplTest.java | 20 ++- .../resources/application-test.properties | 2 + 25 files changed, 773 insertions(+), 119 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/SingleSelectionFieldBuilder.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ZipService.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ZipServiceImpl.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ControllerConfig.java create mode 100644 src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptorTest.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncRunner.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncRunner.java index a825cb93..4b7ebda6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncRunner.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncRunner.java @@ -31,4 +31,9 @@ public class AsyncRunner { return new AsyncResult<>(supplier.get()); } + @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) + public void runAsync(final Runnable block) { + block.run(); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java index 87632603..0677a577 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java @@ -8,8 +8,16 @@ package ch.ethz.seb.sebserver.gbl.async; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.function.Consumer; import java.util.function.Supplier; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -17,6 +25,8 @@ import org.springframework.stereotype.Service; @Service public class AsyncService { + private static final Logger log = LoggerFactory.getLogger(AsyncService.class); + private final AsyncRunner asyncRunner; protected AsyncService(final AsyncRunner asyncRunner) { @@ -62,4 +72,41 @@ public class AsyncService { momoized); } + public void pipeToOutputStream( + final OutputStream output, + final Consumer consumer) { + + this.asyncRunner.runAsync(() -> { + + PipedOutputStream pout = null; + PipedInputStream pin = null; + try { + pout = new PipedOutputStream(); + pin = new PipedInputStream(pout); + + consumer.accept(pout); + + IOUtils.copyLarge(pin, output); + + pin.close(); + pout.flush(); + pout.close(); + + } catch (final IOException e) { + log.error("Error while pipe stream data: ", e); + } finally { + try { + pin.close(); + } catch (final IOException e1) { + log.error("Failed to close PipedInputStream: ", e1); + } + try { + pout.close(); + } catch (final IOException e1) { + log.error("Failed to close PipedOutputStream: ", e1); + } + } + }); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncServiceSpringConfig.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncServiceSpringConfig.java index 847a8fe6..e95be3ad 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncServiceSpringConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncServiceSpringConfig.java @@ -12,10 +12,13 @@ import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration -public class AsyncServiceSpringConfig { +@EnableAsync +public class AsyncServiceSpringConfig implements AsyncConfigurer { public static final String EXECUTOR_BEAN_NAME = "AsyncServiceExecutorBean"; @@ -30,4 +33,9 @@ public class AsyncServiceSpringConfig { return executor; } + @Override + public Executor getAsyncExecutor() { + return threadPoolTaskExecutor(); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java index f3edb939..c8032071 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java @@ -41,7 +41,8 @@ public enum AttributeType { /** Table type is a list of a composite of single types */ TABLE(COMPOSITE_LIST), - ; + + STATIC_TABLE(COMPOSITE_LIST); public final AttributeValueType attributeValueType; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java index 4349aabd..dac89599 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java @@ -14,6 +14,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +/** Adapter interface for SEB Exam Configuration based input fields. */ public interface InputField { ConfigurationAttribute getAttribute(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/SingleSelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/SingleSelectionFieldBuilder.java new file mode 100644 index 00000000..652c20b6 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/SingleSelectionFieldBuilder.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.service.examconfig.impl; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.widget.Selection; +import ch.ethz.seb.sebserver.gui.widget.SingleSelection; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class SingleSelectionFieldBuilder implements InputFieldBuilder { + + private final WidgetFactory widgetFactory; + + protected SingleSelectionFieldBuilder(final WidgetFactory widgetFactory) { + this.widgetFactory = widgetFactory; + } + + @Override + public boolean builderFor( + final ConfigurationAttribute attribute, + final Orientation orientation) { + + return attribute.type == AttributeType.SINGLE_SELECTION; + } + + @Override + public InputField createInputField( + final Composite parent, + final ConfigurationAttribute attribute, + final ViewContext viewContext) { + + final Orientation orientation = viewContext + .getOrientation(attribute.id); + final Composite innerGrid = InputFieldBuilder + .createInnerGrid(parent, orientation); + + final SingleSelection selection = this.widgetFactory.selectionLocalized( + Selection.Type.SINGLE, + innerGrid, + () -> this.getLocalizedResources(attribute)) + .getTypeInstance(); + + final SingleSelectionInputField singleSelectionInputField = new SingleSelectionInputField( + attribute, + orientation, + selection, + InputFieldBuilder.createErrorLabel(innerGrid)); + + selection.setSelectionListener(event -> { + singleSelectionInputField.clearError(); + viewContext.getValueChangeListener().valueChanged( + viewContext, + attribute, + String.valueOf(selection.getSelectionValue()), + singleSelectionInputField.listIndex); + }); + + return null; + } + + private List> getLocalizedResources(final ConfigurationAttribute attribute) { + if (attribute == null) { + return Collections.emptyList(); + } + + final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport(); + final String prefix = ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + "."; + return Arrays.asList(StringUtils.split( + attribute.resources, + Constants.LIST_SEPARATOR)) + .stream() + .map(value -> new Tuple<>( + value, + i18nSupport.getText( + new LocTextKey(prefix + value), + value))) + .collect(Collectors.toList()); + } + + static final class SingleSelectionInputField extends AbstractInputField { + + protected SingleSelectionInputField( + final ConfigurationAttribute attribute, + final Orientation orientation, + final SingleSelection control, + final Label errorLabel) { + + super(attribute, orientation, control, errorLabel); + } + + @Override + public String getValue() { + return this.control.getSelectionValue(); + } + + @Override + protected void setValueToControl(final String value) { + this.control.select(value); + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java index f3e0df5a..5f4fc0a1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java @@ -270,17 +270,6 @@ public class TableFieldBuilder implements InputFieldBuilder { } -// private void applyRowValues( -// final int rowIndex, -// final Map rowValues) { -// -// // set the new values -// this.values.set(rowIndex, rowValues); -// // update table row -// applyTableRowValues(rowIndex); -// -// } - private void applyTableRowValues(final int index) { final TableItem item = this.control.getItem(index); final Map rowValues = this.values.get(index); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java index 0b36b84f..4263cd17 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java @@ -20,6 +20,7 @@ import org.eclipse.swt.widgets.ColorDialog; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +38,8 @@ public class ColorSelection extends Composite implements Selection { private final Composite colorField; private RGB selection; + private Listener listener = null; + ColorSelection(final Composite parent, final WidgetFactory widgetFactory) { super(parent, SWT.NONE); final GridLayout gridLayout = new GridLayout(2, false); @@ -67,6 +70,11 @@ public class ColorSelection extends Composite implements Selection { this.addListener(SWT.Resize, this::adaptColumnWidth); } + @Override + public void setSelectionListener(final Listener listener) { + this.listener = listener; + } + @Override public Type type() { return Type.COLOR; @@ -100,6 +108,9 @@ public class ColorSelection extends Composite implements Selection { this.selection = this.colorDialog.getRGB(); applySelection(); + if (this.listener != null) { + this.listener.handleEvent(event); + } }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java index c7300066..b9f509ab 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java @@ -20,6 +20,7 @@ import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; @@ -34,6 +35,8 @@ public class MultiSelection extends Composite implements Selection { private final List