From 8bddae07110c6748d8b0847256ff4fa40c00c9fb Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Wed, 9 Dec 2020 20:19:39 +0100 Subject: [PATCH] UI for Import/Export of entities --- .../org/libreccm/imexport/ImportExport.java | 44 +++++++++------ .../org/libreccm/imexport/ImportManifest.java | 23 ++++++-- .../ui/admin/imexport/ImExportController.java | 55 ++++++++++++++---- .../ui/admin/imexport/ImExportTasks.java | 25 +-------- .../imexport/ImportExportTaskManager.java | 47 +++++++--------- .../ui/admin/imexport/ImportOption.java | 56 +++++++++++++++++++ .../ui/admin/imexport/ImportTask.java | 13 ++++- .../ui/admin/imexport/ImportTaskStatus.java | 52 +++++++++++++++-- .../components/bootstrap/formGroupRadio.xhtml | 5 -- .../ui/admin/imexport/exporting.xhtml | 36 ------------ .../libreccm/ui/admin/imexport/import.xhtml | 51 +++++++++++++++++ .../org/libreccm/ui/AdminBundle.properties | 6 ++ .../org/libreccm/ui/AdminBundle_de.properties | 6 ++ 13 files changed, 291 insertions(+), 128 deletions(-) create mode 100644 ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportOption.java delete mode 100644 ccm-core/src/main/resources/WEB-INF/views/org/libreccm/ui/admin/imexport/exporting.xhtml create mode 100644 ccm-core/src/main/resources/WEB-INF/views/org/libreccm/ui/admin/imexport/import.xhtml diff --git a/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java b/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java index 1402b6a22..bb79e0d17 100644 --- a/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java +++ b/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java @@ -87,7 +87,7 @@ public class ImportExport { .stream() .collect(Collectors.toList()) ); - + return tree; } catch (DependencyException ex) { throw new UnexpectedErrorException(ex); @@ -402,8 +402,9 @@ public class ImportExport { private ImportManifest createImportManifest(final String path) { - final String manifestPath = String.format("imports/%s/ccm-export.json", - path); + final String manifestPath = String.format( + "imports/%s/ccm-export.json", path + ); try (final InputStream inputStream = ccmFiles .createInputStream(manifestPath)) { @@ -412,24 +413,33 @@ public class ImportExport { final JsonObject manifestJson = reader.readObject(); if (!manifestJson.containsKey("created")) { - throw new IllegalArgumentException(String.format( - "The manifest file \"%s\" is malformed. " - + "Key \"created\" is missing.", - manifestPath)); + throw new IllegalArgumentException( + String.format( + "The manifest file \"%s\" is malformed. " + + "Key \"created\" is missing.", + manifestPath + ) + ); } if (!manifestJson.containsKey("onServer")) { - throw new IllegalArgumentException(String.format( - "The manifest file \"%s\" is malformed. " - + "Key \"onServer\" is missing.", - manifestPath)); + throw new IllegalArgumentException( + String.format( + "The manifest file \"%s\" is malformed. " + + "Key \"onServer\" is missing.", + manifestPath + ) + ); } if (!manifestJson.containsKey("types")) { - throw new IllegalArgumentException(String.format( - "The manifest file \"%s\" is malformed. " - + "Key \"types\" is missing.", - manifestPath)); + throw new IllegalArgumentException( + String.format( + "The manifest file \"%s\" is malformed. " + + "Key \"types\" is missing.", + manifestPath + ) + ); } final LocalDateTime created = LocalDateTime @@ -446,9 +456,11 @@ public class ImportExport { } return new ImportManifest( + path, Date.from(created.atZone(ZoneId.of("UTC")).toInstant()), onServer, - types); + types + ); } catch (IOException | FileAccessException | FileDoesNotExistException diff --git a/ccm-core/src/main/java/org/libreccm/imexport/ImportManifest.java b/ccm-core/src/main/java/org/libreccm/imexport/ImportManifest.java index dbcf6fe66..05c12e78b 100644 --- a/ccm-core/src/main/java/org/libreccm/imexport/ImportManifest.java +++ b/ccm-core/src/main/java/org/libreccm/imexport/ImportManifest.java @@ -23,25 +23,36 @@ import java.util.Date; import java.util.List; /** - * Java class containg the properties of an parsed import manifest. - * + * Java class containg the properties of an parsed import manifest. + * * @author Jens Pelzetter */ public class ImportManifest { + private final String importName; + private final Date created; + private final String onServer; + private final List types; - public ImportManifest(final Date created, - final String onServer, - final List types) { - + public ImportManifest( + final String importName, + final Date created, + final String onServer, + final List types + ) { + this.importName = importName; this.created = created; this.onServer = onServer; this.types = types; } + public String getImportName() { + return importName; + } + public Date getCreated() { return new Date(created.getTime()); } diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportController.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportController.java index faecb9f50..8d96f650c 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportController.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportController.java @@ -23,9 +23,12 @@ import org.libreccm.imexport.AbstractEntityImExporter; import org.libreccm.imexport.EntityImExporterTreeNode; import org.libreccm.imexport.Exportable; import org.libreccm.imexport.ImportExport; +import org.libreccm.imexport.ImportManifest; import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.RequiresPrivilege; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -129,15 +132,6 @@ public class ImExportController { taskManager.exportEntities(exportTypes, exportName); -// models.put( -// "exportEntities", -// exportNodes -// .stream() -// .map(node -> node.getEntityImExporter().getEntityClass().getName()) -// .sorted() -// .collect(Collectors.joining("\n")) -// ); -// return "org/libreccm/ui/admin/imexport/exporting.xhtml"; return "redirect:imexport"; } @@ -146,7 +140,33 @@ public class ImExportController { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) public String importEntities() { - throw new NotFoundException(); + models.put( + "importArchives", + importExport + .listAvailableImportArchivies() + .stream() + .map(this::buildImportOption) + .sorted() + .collect( + Collectors.toMap( + ImportOption::getImportName, + ImportOption::getLabel + ) + ) + ); + return "org/libreccm/ui/admin/imexport/import.xhtml"; + } + + @POST + @Path("/import") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + public String importEntities( + @FormParam("archive") final String importArchive + ) { + taskManager.importEntities(importArchive); + + return "redirect:imexport"; } private String noDuplicateKeys(final String str1, final String str2) { @@ -176,4 +196,19 @@ public class ImExportController { } } + private ImportOption buildImportOption(final ImportManifest manifest) { + return new ImportOption( + manifest.getImportName(), + String.format( + "%s from server %s created on %s with types %s", + manifest.getImportName(), + manifest.getOnServer(), + DateTimeFormatter.ISO_DATE_TIME.withZone( + ZoneOffset.systemDefault() + ).format(manifest.getCreated().toInstant()), + manifest.getTypes().stream().collect(Collectors.joining(", ")) + ) + ); + } + } diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportTasks.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportTasks.java index 51dc46149..87af6baed 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportTasks.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImExportTasks.java @@ -22,14 +22,7 @@ import org.libreccm.imexport.Exportable; import org.libreccm.imexport.ImportExport; import java.util.Collection; -import java.util.concurrent.Future; -import javax.ejb.AsyncResult; -import javax.ejb.Asynchronous; -import javax.ejb.Singleton; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.ObservesAsync; import javax.inject.Inject; @@ -45,22 +38,6 @@ public class ImExportTasks { @Inject private ImportExport importExport; -// @Asynchronous -// @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) -// public Future startExport( -// final Collection entities, -// final String exportName -// ) { -// importExport.exportEntities(entities, exportName); -// return new AsyncResult<>(null); -// } -// -// @Asynchronous -// @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) -// public Future startImport(final String importName) { -// importExport.importEntities(importName); -// return new AsyncResult<>(null); -// } @Transactional(Transactional.TxType.REQUIRED) public ExportTask exportEntities(@ObservesAsync final ExportTask task) { @@ -73,7 +50,7 @@ public class ImExportTasks { } @Transactional(Transactional.TxType.REQUIRED) - public void importEntitites(@ObservesAsync final ImExportTaskStatus task) { + public void importEntitites(@ObservesAsync final ImportTask task) { final String importName = task.getName(); importExport.importEntities(importName); diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java index 2c5313815..0a88afe59 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java @@ -63,12 +63,6 @@ public class ImportExportTaskManager { @Inject private Event importTaskSender; -// @Inject -// private ImExportTasks imExportTasks; - -// private SortedSet exportTasks; -// -// private SortedSet importTasks; private final SortedSet exportTasks; private final SortedSet importTasks; @@ -111,9 +105,6 @@ public class ImportExportTaskManager { final ExportTaskStatus taskStatus = new ExportTaskStatus(); taskStatus.setName(exportName); taskStatus.setStarted(LocalDateTime.now()); -// final Future status = imExportTasks.startExport( -// entities, exportName -// ); exportTaskSender.fireAsync( new ExportTask(exportName, LocalDate.now(), entities, taskStatus) ).handle((task , ex) -> handleExportTaskResult(task, ex, taskStatus)); @@ -122,24 +113,15 @@ public class ImportExportTaskManager { exportTasks.add(taskStatus); } -// public void exportEntities( -// final Collection entities, final String exportName -// ) { -// final ImExportTaskStatus task = new ImExportTaskStatus(); -// task.setName(exportName); -// task.setStarted(LocalDate.now()); -// final Future status = startExport(entities, exportName); -// task.setStatus(status); -// exportTasks.add(task); -// } public void importEntities(final String importName) { -// final ImExportTaskStatus task = new ImExportTaskStatus(); -// task.setName(importName); -// task.setStarted(LocalDateTime.now()); -// final Future status = imExportTasks.startImport(importName); -// task.setStatus(status); -// importTasks.add(task); - throw new UnsupportedOperationException(); + final ImportTaskStatus taskStatus = new ImportTaskStatus(); + taskStatus.setStarted(LocalDateTime.now()); + importTaskSender.fireAsync( + new ImportTask(importName, LocalDate.now(), taskStatus) + ).handle((task, ex) -> handleImportTaskResult(task, ex, taskStatus)); + + taskStatus.setStatus(ImExportTaskStatusEnum.RUNNING); + importTasks.add(taskStatus); } @Schedule(hour = "*", minute = "*/5", persistent = false) @@ -180,4 +162,17 @@ public class ImportExportTaskManager { return task; } + private Object handleImportTaskResult( + final ImportTask task, final Throwable ex, final ImportTaskStatus status + ) { + if (ex == null) { + status.setStatus(ImExportTaskStatusEnum.FINISHED); + } else { + status.setStatus(ImExportTaskStatusEnum.ERROR); + status.setException(ex); + LOGGER.error("Import Task {} failed", task); + LOGGER.error("with exception: ", ex); + } + return task; + } } diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportOption.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportOption.java new file mode 100644 index 000000000..6ffee8514 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportOption.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.ui.admin.imexport; + +import java.util.Objects; + +/** + * + * @author Jens Pelzetter + */ +public class ImportOption implements Comparable { + + private final String importName; + + private final String label; + + public ImportOption( + final String importName, + final String label + ) { + this.importName = importName; + this.label = label; + } + + public String getImportName() { + return importName; + } + + public String getLabel() { + return label; + } + + @Override + public int compareTo(final ImportOption other) { + return importName.compareTo( + Objects.requireNonNull(other).getImportName() + ); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTask.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTask.java index 013c7406c..2ea5bc252 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTask.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTask.java @@ -30,9 +30,16 @@ public class ImportTask { private final LocalDate started; - public ImportTask(final String name, final LocalDate started) { + private final ImportTaskStatus status; + + public ImportTask( + final String name, + final LocalDate started, + final ImportTaskStatus status + ) { this.name = name; this.started = started; + this.status = status; } public String getName() { @@ -43,4 +50,8 @@ public class ImportTask { return started; } + public ImportTaskStatus getStatus() { + return status; + } + } diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTaskStatus.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTaskStatus.java index c67ecafb9..b763d060b 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTaskStatus.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportTaskStatus.java @@ -23,7 +23,6 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Comparator; import java.util.Objects; -import java.util.concurrent.CompletionStage; /** * @@ -35,12 +34,15 @@ public class ImportTaskStatus implements Comparable { private LocalDateTime started; - private CompletionStage status; + private ImExportTaskStatusEnum status; + + private Throwable exception; public String getName() { return name; } + protected void setName(final String name) { this.name = name; } @@ -53,14 +55,56 @@ public class ImportTaskStatus implements Comparable { this.started = started; } - public CompletionStage getStatus() { + public ImExportTaskStatusEnum getStatus() { return status; } - protected void setStatus(final CompletionStage status) { + protected void setStatus(final ImExportTaskStatusEnum status) { this.status = status; } + + public Throwable getException() { + return exception; + } + protected void setException(final Throwable exception) { + this.exception = exception; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + Objects.hashCode(name); + hash = 53 * hash + Objects.hashCode(started); + hash = 53 * hash + Objects.hashCode(status); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ImExportTaskStatus)) { + return false; + } + final ImportTaskStatus other = (ImportTaskStatus) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(name, other.getName())) { + return false; + } + if (!Objects.equals(started, other.getStarted())) { + return false; + } + return status == other.getStatus(); + } + + public boolean canEqual(final Object obj) { return obj instanceof ImExportTaskStatus; } diff --git a/ccm-core/src/main/resources/META-INF/resources/components/bootstrap/formGroupRadio.xhtml b/ccm-core/src/main/resources/META-INF/resources/components/bootstrap/formGroupRadio.xhtml index 726371ec8..0b3717a1b 100644 --- a/ccm-core/src/main/resources/META-INF/resources/components/bootstrap/formGroupRadio.xhtml +++ b/ccm-core/src/main/resources/META-INF/resources/components/bootstrap/formGroupRadio.xhtml @@ -3,11 +3,6 @@ xmlns:cc="http://xmlns.jcp.org/jsf/composite" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"> - - - - - - - - - - - - - - -
-

#{AdminMessages['imexport.export.label']}

- -
#{exportEntities}
-
-
- -
- - - diff --git a/ccm-core/src/main/resources/WEB-INF/views/org/libreccm/ui/admin/imexport/import.xhtml b/ccm-core/src/main/resources/WEB-INF/views/org/libreccm/ui/admin/imexport/import.xhtml new file mode 100644 index 000000000..af59ec114 --- /dev/null +++ b/ccm-core/src/main/resources/WEB-INF/views/org/libreccm/ui/admin/imexport/import.xhtml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + +
+

#{AdminMessages['imexport.import.label']}

+ +
+

#{AdminMessages['imexport.import.help']}

+ + + + #{AdminMessages['imexport.import.cancel']} + + + +
+
+ +
+ \ No newline at end of file diff --git a/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle.properties b/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle.properties index 2de1c0183..bd66256dd 100644 --- a/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle.properties +++ b/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle.properties @@ -556,3 +556,9 @@ imexport.activeimports.table.columns.actions.heading=Actions imexport.activeimports.table.columns.status.finished=Finished imexport.activeimports.table.columns.status.running=In progress imexport.activeimports.table.columns.actions.button_label=Cancel +imexport.import.label=Import +imexport.import.help=Select one of the available import archives +imexport.import.archives.help=Select the archive to import +imexport.import.archives.label=Available import archives +imexport.import.cancel=Cancel +imexport.import.submit=Start Import diff --git a/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle_de.properties b/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle_de.properties index 79c56bf03..c8e8a1711 100644 --- a/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle_de.properties +++ b/ccm-core/src/main/resources/org/libreccm/ui/AdminBundle_de.properties @@ -556,3 +556,9 @@ imexport.activeimports.table.columns.actions.heading=Aktionen imexport.activeimports.table.columns.status.finished=Abgeschlossen imexport.activeimports.table.columns.status.running=In Arbeit imexport.activeimports.table.columns.actions.button_label=Abbrechen +imexport.import.label=Import +imexport.import.help=W\u00e4hlen Sie das zu importierende Archiv +imexport.import.archives.help=W\u00e4hlen Sie das zu importierende Archiv +imexport.import.archives.label=Verf\u00fcgbare Import-Archive +imexport.import.cancel=Abbrechen +imexport.import.submit=Import starten