Skip to content

Commit

Permalink
Save filename mapping to 'renaming' file to use on server outtages
Browse files Browse the repository at this point in the history
  • Loading branch information
solth committed Sep 5, 2023
1 parent cbe1ac7 commit 194324e
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ public void open(String processID, String referringView, String taskId) {
} else {
PrimeFaces.current().executeScript("PF('metadataLockedDialog').show();");
}
if (ServiceManager.getFileService().revertUnsavedRenamings(workpiece, process)) {
PrimeFaces.current().executeScript("PF('revertRenamingDialog').show();");
}
} catch (IOException | DAOException | InvalidImagesException | NoSuchElementException e) {
Helper.setErrorMessage(e.getLocalizedMessage(), logger, e);
}
Expand Down Expand Up @@ -390,7 +393,7 @@ public String closeAndReturn() {
public void close() {
deleteNotSavedUploadedMedia();
unsavedDeletedMedia.clear();
ServiceManager.getFileService().revertRenaming(filenameMapping.inverseBidiMap(), workpiece);
ServiceManager.getFileService().revertRenaming(filenameMapping.inverseBidiMap(), workpiece, process);
metadataPanel.clear();
structurePanel.clear();
workpiece = null;
Expand Down Expand Up @@ -503,6 +506,7 @@ private String save(boolean close) {
filenameMapping = new DualHashBidiMap<>();
ServiceManager.getProcessService().updateChildrenFromLogicalStructure(process, workpiece.getLogicalStructure());
ServiceManager.getFileService().createBackupFile(process);
ServiceManager.getFileService().removeRenamingFile(process);
try (OutputStream out = ServiceManager.getFileService().write(mainFileUri)) {
ServiceManager.getMetsService().save(workpiece, out);
ServiceManager.getProcessService().saveToIndex(process,false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@

package org.kitodo.production.services.file;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystems;
Expand All @@ -33,6 +37,7 @@
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
Expand Down Expand Up @@ -97,6 +102,7 @@ public class FileService {
private static final String TEMP_EXTENSION = ".tmp";

private static final String SLASH = "/";
private static final String RENAMING = ".renaming";


/**
Expand Down Expand Up @@ -1512,24 +1518,78 @@ public int renameMediaFiles(Process process, Workpiece workpiece, DualHashBidiMa
filenameMapping.put(renamingEntry.getKey(), fileManagementModule.rename(tempFilename, newFilepath));
}
}
createRenamingFile(filenameMapping, processDataUri);
return numberOfRenamedMedia;
}

private void createRenamingFile(BidiMap<URI, URI> filenameMapping, URI processDir) throws IOException {
URI renamingFileUri = processDir.resolve(RENAMING);
try (OutputStream outputStream = write(renamingFileUri);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream))) {
String mappingContent = filenameMapping.entrySet().stream()
.map(entry -> entry.getKey() + StringUtils.SPACE + entry.getValue())
.collect(Collectors.joining(System.lineSeparator()));
bufferedWriter.write(mappingContent);
} catch (IOException e) {
logger.error(e.getMessage());
throw e;
}
}

private DualHashBidiMap<URI, URI> readRenamingFile(Process process) {
DualHashBidiMap<URI, URI> renamingMapping = new DualHashBidiMap<>();
URI processDataUri = ServiceManager.getProcessService().getProcessDataDirectory(process);
URI renamingFileUri = processDataUri.resolve(RENAMING);
try (InputStream inputStream = read(renamingFileUri);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
for (String line : bufferedReader.lines().collect(Collectors.toList())) {
String[] lineFragments = line.split(StringUtils.SPACE);
if (lineFragments.length > 2) {
// TODO: should parsing stop with an error dialog?
logger.error(String.format("Unable to parse line '%s' for filename mapping", line));
continue;
}
try {
renamingMapping.put(new URI(lineFragments[0]), new URI(lineFragments[1]));
} catch (URISyntaxException e) {
// TODO: should parsing stop with an error dialog?
logger.error(e.getMessage());
}
}
} catch (IOException e) {
logger.info(e.getMessage());
}
return renamingMapping;
}

public void removeRenamingFile(Process process) throws IOException {
URI processDataUri = ServiceManager.getProcessService().getProcessDataDirectory(process);

if (!processDataUri.toString().endsWith(SLASH)) {
processDataUri = URI.create(processDataUri + SLASH);
}
URI renamingFileUri = processDataUri.resolve(RENAMING);
delete(renamingFileUri);
}

/**
* Revert renaming of media files when the user leaves the metadata editor without saving. This method uses a
* provided map object to rename media files identified by the map entries values to the corresponding map entries
* keys.
*
* @param filenameMappings Bidirectional map containing original filenames as keys and new filenames as values.
* @param workpiece Workpiece of current process
* @param process Process current process
*/
public void revertRenaming(BidiMap<URI, URI> filenameMappings, Workpiece workpiece) {
public void revertRenaming(BidiMap<URI, URI> filenameMappings, Workpiece workpiece, Process process) {
// revert media variant URIs for all media files in workpiece to previous, original values
for (PhysicalDivision physicalDivision : workpiece
.getAllPhysicalDivisionChildrenFilteredByTypes(PhysicalDivision.TYPES)) {
for (Entry<MediaVariant, URI> mediaVariantURIEntry : physicalDivision.getMediaFiles().entrySet()) {
physicalDivision.getMediaFiles().put(mediaVariantURIEntry.getKey(),
filenameMappings.get(mediaVariantURIEntry.getValue()));
if (filenameMappings.containsKey(mediaVariantURIEntry.getValue())) {
physicalDivision.getMediaFiles().put(mediaVariantURIEntry.getKey(),
filenameMappings.get(mediaVariantURIEntry.getValue()));
}
}
}
// revert filenames of media files to previous, original values
Expand All @@ -1542,6 +1602,7 @@ public void revertRenaming(BidiMap<URI, URI> filenameMappings, Workpiece workpie
for (URI tempUri : tempUris) {
fileManagementModule.rename(tempUri, StringUtils.removeEnd(tempUri.toString(), TEMP_EXTENSION));
}
removeRenamingFile(process);
} catch (IOException e) {
logger.error(e);
}
Expand All @@ -1564,4 +1625,20 @@ public DualHashBidiMap<URI, URI> removeUnsavedUploadMediaUriFromFileMapping(URI
}
return updatedMap;
}

/**
* Reads potential '.renaming' file from process directory and reverts all filename changes logged in this file.
* @param workpiece the workpiece of the process
* @param process the process in question
* @return whether any file renaming information could be retrieved from a '.renaming' file of this process or not.
*/
public boolean revertUnsavedRenamings(Workpiece workpiece, Process process) {
BidiMap<URI, URI> filenameMappings = readRenamingFile(process);
if (!filenameMappings.isEmpty()) {
revertRenaming(filenameMappings, workpiece, process);
return true;
} else {
return false;
}
}
}
2 changes: 2 additions & 0 deletions Kitodo/src/main/resources/messages/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ dataEditor.renameMedia=Medien umbenennen
dataEditor.renamingMediaComplete=Das Umbenennen der Medien ist abgeschlossen
dataEditor.renamingMediaError=Beim Umbenennen der Medien ist ein Fehler aufgetreten
dataEditor.renamingMediaText={0} Mediendateien in {1} Ordnern wurden umbenannt. Bitte Speichern Sie den Vorgang. Andernfalls werden die Dateiumbennungen beim Schlie\u00DFen des Metadateneditors verworfen.
dataEditor.revertUnsavedRenamingTitle=Ungespeicherte Dateiumbenennungen r\u00FCckg\u00E4ngig gemacht
dataEditor.revertUnsavedRenamingText=Im Vorgangsverzeichnis wurde eine Datei '.renaming' mit Umbenennungen von Mediendateien gefunden. Dies deutet auf ein unsachgem\u00E4sses Herunterfahren des Servers hin, bevor der Vorgang mit den umbenannten Mediendateien im Editor abgespeichert wurde. Die in der Datei vorgefundenen Umbenennungen wurden daher r\u00FCckg\u00E4ngig gemacht und die '.renaming'-Datei gel\u00F6scht.
dataEditor.structure.customizeDisplay=Anzeige anpassen
dataEditor.structureTree.collapseAll=Strukturbaum komplett einklappen
dataEditor.structureTree.expandAll=Strukturbaum komplett ausklappen
Expand Down
4 changes: 3 additions & 1 deletion Kitodo/src/main/resources/messages/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ dataEditor.layoutSavedSuccessfullyText=Your current editor settings have been sa
dataEditor.renameMedia=Rename media
dataEditor.renamingMediaComplete=Finished renaming media
dataEditor.renamingMediaError=An error occurred while renaming media files
dataEditor.renamingMediaText={0} media files in {1} folders have been renamed. Please click the 'Save' button to persist changes to the filenames. Otherwise the renaming will be reverted upon closing the editor.
dataEditor.renamingMediaText={0} media files in {1} folders have been renamed. Please click the 'Save' button to persist changes to the filenames. Otherwise, the renaming will be reverted upon closing the editor.
dataEditor.revertUnsavedRenamingTitle=Reverted filenames of unsaved renamed media files
dataEditor.revertUnsavedRenamingText=The process directory contained a file '.renaming' with filename with information about renamed media files. This indicates this process contains renamed media files but has not been saved in the metadata editor before a server shutdown occurred. Therefor the original names of the corresponding media files have been restored according to the '.renaming' file and the file itself has been removed.
dataEditor.structure.customizeDisplay=Customize display
dataEditor.structureTree.collapseAll=Collapse all
dataEditor.structureTree.expandAll=Expand all
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--
*
* (c) Kitodo. Key to digital objects e. V. <[email protected]>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*
-->

<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<p:dialog id="revertRenamingDialog"
widgetVar="revertRenamingDialog"
width="500px"
showHeader="false"
appendTo="@(body)"
resizable="false"
closable="false"
modal="true">
<h:form id="confirmForm">
<h:panelGroup id="messageWrapper">
<h3>#{msgs['dataEditor.revertUnsavedRenamingTitle']}</h3>
<h:panelGroup layout="block"
styleClass="select-note ui-messages-error">
<h:outputText value="#{msgs['dataEditor.revertUnsavedRenamingText']}"/>
</h:panelGroup>
</h:panelGroup>
<h:panelGroup layout="block"
styleClass="dialogButtonWrapper">
<p:commandButton value="#{msgs['ok']}"
id="okButton"
onclick="PF('revertRenamingDialog').hide();"
styleClass="primary right"
icon="fa fa-close"
iconPos="right"/>
</h:panelGroup>
</h:form>
</p:dialog>
</ui:composition>
1 change: 1 addition & 0 deletions Kitodo/src/main/webapp/pages/metadataEditor.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@
<ui:include src="/WEB-INF/templates/includes/metadataEditor/dialogs/fileReferencesUpdatedDialog.xhtml"/>
<ui:include src="/WEB-INF/templates/includes/metadataEditor/dialogs/dataEditorSettingSavingResult.xhtml"/>
<ui:include src="/WEB-INF/templates/includes/metadataEditor/dialogs/renamingMediaResults.xhtml"/>
<ui:include src="/WEB-INF/templates/includes/metadataEditor/dialogs/revertRenamingDialog.xhtml"/>
</ui:define>

<ui:define name="breadcrumbs">
Expand Down

0 comments on commit 194324e

Please sign in to comment.