[Fix] first write files to a temporary path, then rename to actual path

this solves issues where files are not written properly on power failure.
This commit is contained in:
Robert von Burg 2019-05-12 20:18:01 +02:00
parent 3a009a6857
commit ed2526ec95
1 changed files with 24 additions and 10 deletions

View File

@ -50,16 +50,18 @@ public class FileIo {
private static final Logger logger = LoggerFactory.getLogger(FileIo.class); private static final Logger logger = LoggerFactory.getLogger(FileIo.class);
private final File path; private final File path;
private final File tmpPath;
public FileIo(File path) { public FileIo(File path) {
this.path = path; this.path = path;
this.tmpPath = new File(this.path.getParentFile(), ".tmp_" + this.path.getName());
} }
public <T> void writeSax(PersistenceContext<T> ctx) { public <T> void writeSax(PersistenceContext<T> ctx) {
XMLStreamWriter writer = null; XMLStreamWriter writer;
try { try {
try (FileWriter fileWriter = new FileWriter(this.path);) { try (FileWriter fileWriter = new FileWriter(this.tmpPath)) {
XMLOutputFactory factory = XMLOutputFactory.newInstance(); XMLOutputFactory factory = XMLOutputFactory.newInstance();
writer = factory.createXMLStreamWriter(fileWriter); writer = factory.createXMLStreamWriter(fileWriter);
@ -78,9 +80,16 @@ public class FileIo {
writer.flush(); writer.flush();
} }
if (!this.tmpPath.renameTo(this.path)) {
throw new IllegalStateException(
"Failed to rename temp file " + this.tmpPath.getName() + " to " + this.path.getAbsolutePath());
}
} catch (FactoryConfigurationError | XMLStreamException | IOException e) { } catch (FactoryConfigurationError | XMLStreamException | IOException e) {
if (this.path.exists()) if (this.tmpPath.exists()) {
this.path.delete(); if (!this.tmpPath.delete())
logger.error("Failed to delete existing temp file " + this.tmpPath.getAbsolutePath());
}
String msg = "Writing to file failed due to internal error: {0}"; //$NON-NLS-1$ String msg = "Writing to file failed due to internal error: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, e.getMessage()); msg = MessageFormat.format(msg, e.getMessage());
throw new XmlException(msg, e); throw new XmlException(msg, e);
@ -149,20 +158,25 @@ public class FileIo {
// transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t"); // transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t");
// Transform to file // Transform to file
StreamResult result = new StreamResult(this.path); StreamResult result = new StreamResult(this.tmpPath);
Source xmlSource = new DOMSource(document); Source xmlSource = new DOMSource(document);
transformer.transform(xmlSource, result); transformer.transform(xmlSource, result);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
String msg = MessageFormat.format("Wrote DOM to {0}", this.path.getAbsolutePath()); //$NON-NLS-1$ String msg = MessageFormat.format("Wrote DOM to {0}", this.tmpPath.getAbsolutePath()); //$NON-NLS-1$
logger.info(msg); logger.info(msg);
} }
if (!this.tmpPath.renameTo(this.path)) {
throw new IllegalStateException(
"Failed to rename temp file " + this.tmpPath.getName() + " to " + this.path.getAbsolutePath());
}
} catch (TransformerFactoryConfigurationError | TransformerException e) { } catch (TransformerFactoryConfigurationError | TransformerException e) {
if (this.tmpPath.exists()) {
if (this.path.exists()) if (!this.tmpPath.delete())
this.path.delete(); logger.error("Failed to delete existing temp file " + this.tmpPath.getAbsolutePath());
}
String msg = "Writing to file failed due to internal error: {0}"; //$NON-NLS-1$ String msg = "Writing to file failed due to internal error: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, e.getMessage()); msg = MessageFormat.format(msg, e.getMessage());
throw new XmlException(msg, e); throw new XmlException(msg, e);