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.user.UserInfo;
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.form.Form;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
@ -278,7 +277,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
pageService.getWidgetFactory())
.setDialogWidth(600);
final ImportFormBuilder importFormBuilder = new ImportFormBuilder(
final ImportFormContext importFormContext = new ImportFormContext(
pageService,
action.pageContext());
@ -287,8 +286,8 @@ public class SebExamConfigPropForm implements TemplateComposer {
formHandle -> SebExamConfigPropForm.doImport(
pageService,
formHandle),
Utils.EMPTY_EXECUTION,
importFormBuilder);
importFormContext::cancelUpload,
importFormContext);
return action;
};
@ -303,7 +302,8 @@ public class SebExamConfigPropForm implements TemplateComposer {
final Control fieldControl = form.getFieldControl(API.IMPORT_FILE_ATTR_NAME);
final PageContext context = formHandle.getContext();
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) {
final Configuration configuration = pageService.getRestService()
.getBuilder(ImportExamConfig.class)
@ -313,7 +313,10 @@ public class SebExamConfigPropForm implements TemplateComposer {
form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
.withBody(inputStream)
.call()
.get(context::notifyError);
.get(e -> {
fileUpload.close();
return context.notifyError(e);
});
if (configuration != null) {
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 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.pageContext = pageContext;
}
@ -353,8 +358,18 @@ public class SebExamConfigPropForm implements TemplateComposer {
"").asPasswordField())
.build();
this.form = formHandle.getForm();
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.widgets.Composite;
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.gui.service.i18n.I18nSupport;
@ -34,6 +36,8 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
public class FileUploadSelection extends Composite {
private static final Logger log = LoggerFactory.getLogger(FileUploadSelection.class);
private static final long serialVersionUID = 5800153475027387363L;
private static final LocTextKey PLEASE_SELECT_TEXT =
@ -48,6 +52,8 @@ public class FileUploadSelection extends Composite {
private Consumer<String> errorHandler;
private InputStream inputStream;
private final FileUploadHandler uploadHandler;
private final InputReceiver inputReceiver;
public FileUploadSelection(
final Composite parent,
@ -70,12 +76,15 @@ public class FileUploadSelection extends Composite {
this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT));
this.fileName.setLayoutData(new GridData());
this.fileUpload = null;
this.uploadHandler = null;
this.inputReceiver = null;
} else {
this.fileUpload = new FileUpload(this, SWT.NONE);
this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay()));
this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
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.setText(i18nSupport.getText(PLEASE_SELECT_TEXT));
@ -93,7 +102,7 @@ public class FileUploadSelection extends Composite {
}
return;
}
FileUploadSelection.this.fileUpload.submit(uploadHandler.getUploadUrl());
FileUploadSelection.this.fileUpload.submit(this.uploadHandler.getUploadUrl());
FileUploadSelection.this.fileName.setText(fileName);
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() {
if (this.fileName != null) {
return this.fileName.getText();
@ -149,21 +172,37 @@ public class FileUploadSelection extends Composite {
}
private final class InputReceiver extends FileUploadReceiver {
private PipedInputStream pIn = null;
private PipedOutputStream pOut = null;
@Override
public void receive(final InputStream stream, final FileDetails details) throws IOException {
final PipedInputStream pIn = new PipedInputStream();
final PipedOutputStream pOut = new PipedOutputStream(pIn);
if (this.pIn != null || this.pOut != null) {
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 {
IOUtils.copyLarge(stream, pOut);
IOUtils.copyLarge(stream, this.pOut);
} catch (final Exception e) {
e.printStackTrace();
log.warn("IO error: {}", e.getMessage());
} 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;
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
if (attribute.type == AttributeType.MULTI_CHECKBOX_SELECTION
|| attribute.type == AttributeType.MULTI_SELECTION) {

View file

@ -331,11 +331,15 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
.getOrThrow();
Future<Exception> streamDecrypted = null;
InputStream cryptIn = null;
PipedInputStream plainIn = null;
PipedOutputStream cryptOut = null;
InputStream unzippedIn = null;
try {
final InputStream cryptIn = this.examConfigIO.unzip(input);
final PipedInputStream plainIn = new PipedInputStream();
final PipedOutputStream cryptOut = new PipedOutputStream(plainIn);
cryptIn = this.examConfigIO.unzip(input);
plainIn = new PipedInputStream();
cryptOut = new PipedOutputStream(plainIn);
// decrypt
streamDecrypted = this.sebConfigEncryptionService.streamDecrypted(
@ -344,11 +348,11 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
EncryptionContext.contextOf(password));
// if zipped, unzip attach unzip stream first
final InputStream _plainIn = this.examConfigIO.unzip(plainIn);
unzippedIn = this.examConfigIO.unzip(plainIn);
// parse XML and import
this.examConfigIO.importPlainXML(
_plainIn,
unzippedIn,
newConfig.institutionId,
newConfig.id);
@ -369,6 +373,11 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
}
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 {
try {
out.flush();
out.close();
} catch (final IOException e) {
log.error("Failed to close OutputStream: ", e);
}
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(zipInputStream);
if (log.isDebugEnabled()) {
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;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.mybatis.dynamic.sql.SqlTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
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 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.model.Domain.EXAM;
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.ConfigurationNode;
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.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
@ -167,7 +174,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Configuration importExamConfig(
public Object importExamConfig(
@PathVariable final Long modelId,
@RequestHeader(name = API.IMPORT_PASSWORD_ATTR_NAME, required = false) final String password,
@RequestParam(
@ -176,11 +183,27 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
final HttpServletRequest request) throws IOException {
return this.sebExamConfigService.importFromSEBFile(
modelId,
request.getInputStream(),
password)
.getOrThrow();
final InputStream inputStream = new BufferedInputStream(request.getInputStream());
try {
return this.sebExamConfigService.importFromSEBFile(
modelId,
inputStream,
password)
.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_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL,
`lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL,
PRIMARY KEY (`id`),
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_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL,
`lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL,
PRIMARY KEY (`id`),
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_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL,
`lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL,
PRIMARY KEY (`id`),
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_rest_api_token` VARCHAR(4000) NULL,
`lms_proxy_auth_type` VARCHAR(45) NOT NULL,
`lms_proxy_auth_username` VARCHAR(4000) NULL,
`lms_proxy_auth_secret` VARCHAR(4000) NULL,
`lms_proxy_auth_username` VARCHAR(255) NULL,
`lms_proxy_auth_secret` VARCHAR(255) NULL,
`active` INT(1) NOT NULL,
PRIMARY KEY (`id`),
INDEX `setupInstitutionRef_idx` (`institution_id` ASC),