fixed import bugs

This commit is contained in:
anhefti 2019-10-14 15:41:56 +02:00
parent dc7df0620c
commit 258ba2939f
10 changed files with 129 additions and 36 deletions

View file

@ -33,7 +33,6 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.Form; import ch.ethz.seb.sebserver.gui.form.Form;
import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormBuilder;
@ -278,7 +277,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
pageService.getWidgetFactory()) pageService.getWidgetFactory())
.setDialogWidth(600); .setDialogWidth(600);
final ImportFormBuilder importFormBuilder = new ImportFormBuilder( final ImportFormContext importFormContext = new ImportFormContext(
pageService, pageService,
action.pageContext()); action.pageContext());
@ -287,8 +286,8 @@ public class SebExamConfigPropForm implements TemplateComposer {
formHandle -> SebExamConfigPropForm.doImport( formHandle -> SebExamConfigPropForm.doImport(
pageService, pageService,
formHandle), formHandle),
Utils.EMPTY_EXECUTION, importFormContext::cancelUpload,
importFormBuilder); importFormContext);
return action; return action;
}; };
@ -303,7 +302,8 @@ public class SebExamConfigPropForm implements TemplateComposer {
final Control fieldControl = form.getFieldControl(API.IMPORT_FILE_ATTR_NAME); final Control fieldControl = form.getFieldControl(API.IMPORT_FILE_ATTR_NAME);
final PageContext context = formHandle.getContext(); final PageContext context = formHandle.getContext();
if (fieldControl != null && fieldControl instanceof FileUploadSelection) { if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
final InputStream inputStream = ((FileUploadSelection) fieldControl).getInputStream(); final FileUploadSelection fileUpload = (FileUploadSelection) fieldControl;
final InputStream inputStream = fileUpload.getInputStream();
if (inputStream != null) { if (inputStream != null) {
final Configuration configuration = pageService.getRestService() final Configuration configuration = pageService.getRestService()
.getBuilder(ImportExamConfig.class) .getBuilder(ImportExamConfig.class)
@ -313,7 +313,10 @@ public class SebExamConfigPropForm implements TemplateComposer {
form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME)) form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
.withBody(inputStream) .withBody(inputStream)
.call() .call()
.get(context::notifyError); .get(e -> {
fileUpload.close();
return context.notifyError(e);
});
if (configuration != null) { if (configuration != null) {
context.publishInfo(FORM_IMPORT_CONFIRM_TEXT_KEY); context.publishInfo(FORM_IMPORT_CONFIRM_TEXT_KEY);
@ -326,12 +329,14 @@ public class SebExamConfigPropForm implements TemplateComposer {
} }
} }
private static final class ImportFormBuilder implements ModalInputDialogComposer<FormHandle<ConfigurationNode>> { private static final class ImportFormContext implements ModalInputDialogComposer<FormHandle<ConfigurationNode>> {
private final PageService pageService; private final PageService pageService;
private final PageContext pageContext; private final PageContext pageContext;
protected ImportFormBuilder(final PageService pageService, final PageContext pageContext) { private Form form = null;
protected ImportFormContext(final PageService pageService, final PageContext pageContext) {
this.pageService = pageService; this.pageService = pageService;
this.pageContext = pageContext; this.pageContext = pageContext;
} }
@ -353,8 +358,18 @@ public class SebExamConfigPropForm implements TemplateComposer {
"").asPasswordField()) "").asPasswordField())
.build(); .build();
this.form = formHandle.getForm();
return () -> formHandle; return () -> formHandle;
} }
void cancelUpload() {
if (this.form != null) {
final Control fieldControl = this.form.getFieldControl(API.IMPORT_FILE_ATTR_NAME);
if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
((FileUploadSelection) fieldControl).close();
}
}
}
} }
} }

View file

@ -27,6 +27,8 @@ import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
@ -34,6 +36,8 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
public class FileUploadSelection extends Composite { public class FileUploadSelection extends Composite {
private static final Logger log = LoggerFactory.getLogger(FileUploadSelection.class);
private static final long serialVersionUID = 5800153475027387363L; private static final long serialVersionUID = 5800153475027387363L;
private static final LocTextKey PLEASE_SELECT_TEXT = private static final LocTextKey PLEASE_SELECT_TEXT =
@ -48,6 +52,8 @@ public class FileUploadSelection extends Composite {
private Consumer<String> errorHandler; private Consumer<String> errorHandler;
private InputStream inputStream; private InputStream inputStream;
private final FileUploadHandler uploadHandler;
private final InputReceiver inputReceiver;
public FileUploadSelection( public FileUploadSelection(
final Composite parent, final Composite parent,
@ -70,12 +76,15 @@ public class FileUploadSelection extends Composite {
this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT)); this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT));
this.fileName.setLayoutData(new GridData()); this.fileName.setLayoutData(new GridData());
this.fileUpload = null; this.fileUpload = null;
this.uploadHandler = null;
this.inputReceiver = null;
} else { } else {
this.fileUpload = new FileUpload(this, SWT.NONE); this.fileUpload = new FileUpload(this, SWT.NONE);
this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay())); this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay()));
this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
this.fileUpload.setToolTipText(this.i18nSupport.getText(PLEASE_SELECT_TEXT)); this.fileUpload.setToolTipText(this.i18nSupport.getText(PLEASE_SELECT_TEXT));
final FileUploadHandler uploadHandler = new FileUploadHandler(new InputReceiver()); this.inputReceiver = new InputReceiver();
this.uploadHandler = new FileUploadHandler(this.inputReceiver);
this.fileName = new Label(this, SWT.NONE); this.fileName = new Label(this, SWT.NONE);
this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT)); this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT));
@ -93,7 +102,7 @@ public class FileUploadSelection extends Composite {
} }
return; return;
} }
FileUploadSelection.this.fileUpload.submit(uploadHandler.getUploadUrl()); FileUploadSelection.this.fileUpload.submit(this.uploadHandler.getUploadUrl());
FileUploadSelection.this.fileName.setText(fileName); FileUploadSelection.this.fileName.setText(fileName);
FileUploadSelection.this.errorHandler.accept(null); FileUploadSelection.this.errorHandler.accept(null);
}); });
@ -101,6 +110,20 @@ public class FileUploadSelection extends Composite {
} }
} }
public void close() {
if (this.inputReceiver != null) {
this.inputReceiver.close();
}
}
@Override
public void dispose() {
if (this.uploadHandler != null) {
this.uploadHandler.dispose();
}
super.dispose();
}
public String getFileName() { public String getFileName() {
if (this.fileName != null) { if (this.fileName != null) {
return this.fileName.getText(); return this.fileName.getText();
@ -149,21 +172,37 @@ public class FileUploadSelection extends Composite {
} }
private final class InputReceiver extends FileUploadReceiver { private final class InputReceiver extends FileUploadReceiver {
private PipedInputStream pIn = null;
private PipedOutputStream pOut = null;
@Override @Override
public void receive(final InputStream stream, final FileDetails details) throws IOException { public void receive(final InputStream stream, final FileDetails details) throws IOException {
final PipedInputStream pIn = new PipedInputStream(); if (this.pIn != null || this.pOut != null) {
final PipedOutputStream pOut = new PipedOutputStream(pIn); throw new IllegalStateException("InputReceiver already in use");
}
FileUploadSelection.this.inputStream = pIn; this.pIn = new PipedInputStream();
this.pOut = new PipedOutputStream(this.pIn);
FileUploadSelection.this.inputStream = this.pIn;
try { try {
IOUtils.copyLarge(stream, pOut); IOUtils.copyLarge(stream, this.pOut);
} catch (final Exception e) { } catch (final Exception e) {
e.printStackTrace(); log.warn("IO error: {}", e.getMessage());
} finally { } finally {
IOUtils.closeQuietly(pOut); close();
} }
} }
void close() {
try {
this.pOut.flush();
} catch (final Exception e) {
log.error("Unexpected error while trying to flush: ", e);
}
IOUtils.closeQuietly(this.pOut);
}
} }
} }

View file

@ -241,6 +241,11 @@ public class ExamConfigImportHandler extends DefaultHandler {
: top.name; : top.name;
final ConfigurationAttribute attribute = this.attributeResolver.apply(attrName); final ConfigurationAttribute attribute = this.attributeResolver.apply(attrName);
if (attribute == null) {
log.warn("*********************** Save null value: {}", attrName);
return;
}
// check if we have a simple values array // check if we have a simple values array
if (attribute.type == AttributeType.MULTI_CHECKBOX_SELECTION if (attribute.type == AttributeType.MULTI_CHECKBOX_SELECTION
|| attribute.type == AttributeType.MULTI_SELECTION) { || attribute.type == AttributeType.MULTI_SELECTION) {

View file

@ -331,11 +331,15 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
.getOrThrow(); .getOrThrow();
Future<Exception> streamDecrypted = null; Future<Exception> streamDecrypted = null;
InputStream cryptIn = null;
PipedInputStream plainIn = null;
PipedOutputStream cryptOut = null;
InputStream unzippedIn = null;
try { try {
final InputStream cryptIn = this.examConfigIO.unzip(input); cryptIn = this.examConfigIO.unzip(input);
final PipedInputStream plainIn = new PipedInputStream(); plainIn = new PipedInputStream();
final PipedOutputStream cryptOut = new PipedOutputStream(plainIn); cryptOut = new PipedOutputStream(plainIn);
// decrypt // decrypt
streamDecrypted = this.sebConfigEncryptionService.streamDecrypted( streamDecrypted = this.sebConfigEncryptionService.streamDecrypted(
@ -344,11 +348,11 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
EncryptionContext.contextOf(password)); EncryptionContext.contextOf(password));
// if zipped, unzip attach unzip stream first // if zipped, unzip attach unzip stream first
final InputStream _plainIn = this.examConfigIO.unzip(plainIn); unzippedIn = this.examConfigIO.unzip(plainIn);
// parse XML and import // parse XML and import
this.examConfigIO.importPlainXML( this.examConfigIO.importPlainXML(
_plainIn, unzippedIn,
newConfig.institutionId, newConfig.institutionId,
newConfig.id); newConfig.id);
@ -369,6 +373,11 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
} }
throw new RuntimeException("Failed to import SEB configuration. Cause is: " + e.getMessage()); throw new RuntimeException("Failed to import SEB configuration. Cause is: " + e.getMessage());
} finally {
IOUtils.closeQuietly(cryptIn);
IOUtils.closeQuietly(plainIn);
IOUtils.closeQuietly(cryptOut);
IOUtils.closeQuietly(unzippedIn);
} }
}); });
} }

View file

@ -81,11 +81,13 @@ public class ZipServiceImpl implements ZipService {
} finally { } finally {
try { try {
out.flush(); out.flush();
out.close();
} catch (final IOException e) { } catch (final IOException e) {
log.error("Failed to close OutputStream: ", e); log.error("Failed to close OutputStream: ", e);
} }
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(zipInputStream);
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("*** Finish streaming asynchronous unzipping of SEB exam configuration data"); log.debug("*** Finish streaming asynchronous unzipping of SEB exam configuration data");
} }

View file

@ -8,17 +8,22 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.BufferedInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.SqlTable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -27,12 +32,14 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
@ -167,7 +174,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
method = RequestMethod.POST, method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Configuration importExamConfig( public Object importExamConfig(
@PathVariable final Long modelId, @PathVariable final Long modelId,
@RequestHeader(name = API.IMPORT_PASSWORD_ATTR_NAME, required = false) final String password, @RequestHeader(name = API.IMPORT_PASSWORD_ATTR_NAME, required = false) final String password,
@RequestParam( @RequestParam(
@ -176,11 +183,27 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
final HttpServletRequest request) throws IOException { final HttpServletRequest request) throws IOException {
final InputStream inputStream = new BufferedInputStream(request.getInputStream());
try {
return this.sebExamConfigService.importFromSEBFile( return this.sebExamConfigService.importFromSEBFile(
modelId, modelId,
request.getInputStream(), inputStream,
password) password)
.getOrThrow(); .getOrThrow();
} catch (final Exception e) {
// NOTE: It seems that this has to be manually closed on error case
// We expected that this is closed by the API but if this manual close is been left
// some left-overs will affect strange behavior.
// TODO: find a better solution for this
IOUtils.closeQuietly(inputStream);
//throw e;
return new ResponseEntity<>(
Arrays.asList(APIMessage.ErrorMessage.UNEXPECTED.of(e.getMessage())),
Utils.createJsonContentHeader(),
HttpStatus.BAD_REQUEST);
}
} }
} }

View file

@ -35,8 +35,8 @@ CREATE TABLE IF NOT EXISTS `lms_setup` (
`lms_clientsecret` VARCHAR(4000) NULL, `lms_clientsecret` VARCHAR(4000) NULL,
`lms_rest_api_token` VARCHAR(4000) NULL, `lms_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL, `lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL, `lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL, `lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL, `active` INT(1) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `setupInstitutionRef_idx` (`institution_id` ASC), INDEX `setupInstitutionRef_idx` (`institution_id` ASC),

View file

@ -50,8 +50,8 @@ CREATE TABLE IF NOT EXISTS `lms_setup` (
`lms_clientsecret` VARCHAR(4000) NULL, `lms_clientsecret` VARCHAR(4000) NULL,
`lms_rest_api_token` VARCHAR(4000) NULL, `lms_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL, `lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL, `lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL, `lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL, `active` INT(1) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `setupInstitutionRef_idx` (`institution_id` ASC), INDEX `setupInstitutionRef_idx` (`institution_id` ASC),

View file

@ -41,8 +41,8 @@ CREATE TABLE IF NOT EXISTS `lms_setup` (
`lms_clientsecret` VARCHAR(4000) NULL, `lms_clientsecret` VARCHAR(4000) NULL,
`lms_rest_api_token` VARCHAR(4000) NULL, `lms_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL, `lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL, `lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL, `lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL, `active` INT(1) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `setupInstitutionRef_idx` (`institution_id` ASC), INDEX `setupInstitutionRef_idx` (`institution_id` ASC),

View file

@ -31,8 +31,8 @@ CREATE TABLE IF NOT EXISTS `lms_setup` (
`lms_clientsecret` VARCHAR(4000) NULL, `lms_clientsecret` VARCHAR(4000) NULL,
`lms_rest_api_token` VARCHAR(4000) NULL, `lms_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL, `lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL, `lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL, `lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL, `active` INT(1) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `setupInstitutionRef_idx` (`institution_id` ASC), INDEX `setupInstitutionRef_idx` (`institution_id` ASC),