StandardCsvWriter.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2015-2016 ForgeRock AS.
*/
package org.forgerock.audit.handlers.csv;
import static org.forgerock.util.Reject.checkNotNull;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import org.forgerock.audit.events.handlers.writers.AsynchronousTextWriter;
import org.forgerock.audit.events.handlers.writers.RotatableWriter;
import org.forgerock.audit.events.handlers.writers.TextWriter;
import org.forgerock.audit.events.handlers.writers.TextWriterAdapter;
import org.forgerock.audit.handlers.csv.CsvAuditEventHandlerConfiguration.EventBufferingConfiguration;
import org.forgerock.util.Reject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.supercsv.io.CsvMapReader;
import org.supercsv.io.ICsvMapReader;
import org.supercsv.prefs.CsvPreference;
/**
* Responsible for writing to a CSV file.
*/
class StandardCsvWriter implements CsvWriter {
private static final Logger logger = LoggerFactory.getLogger(StandardCsvWriter.class);
private final CsvFormatter csvFormatter;
private final String[] headers;
private final Writer csvWriter;
private RotatableWriter rotatableWriter;
StandardCsvWriter(File csvFile, String[] headers, CsvPreference csvPreference,
CsvAuditEventHandlerConfiguration config) throws IOException {
Reject.ifTrue(config.getSecurity().isEnabled(), "StandardCsvWriter should not be used if security is enabled");
boolean fileAlreadyInitialized = csvFile.exists();
if (fileAlreadyInitialized) {
try (ICsvMapReader reader = new CsvMapReader(new BufferedReader(new FileReader(csvFile)), csvPreference)) {
final String[] actualHeaders = reader.getHeader(true);
// Assert that the 2 headers equals.
if (actualHeaders == null) {
fileAlreadyInitialized = false;
} else {
if (actualHeaders.length != headers.length) {
throw new IOException("Resuming an existing CSV file but the headers do not match.");
}
for (int idx = 0; idx < actualHeaders.length; idx++) {
if (!actualHeaders[idx].equals(headers[idx])) {
throw new IOException("Resuming an existing CSV file but the headers do not match.");
}
}
}
}
}
this.headers = checkNotNull(headers, "The headers can't be null.");
csvFormatter = new CsvFormatter(csvPreference);
csvWriter = constructWriter(csvFile, fileAlreadyInitialized, config);
if (rotatableWriter != null) {
rotatableWriter.registerRotationHooks(new CsvRotationHooks(csvFormatter, headers));
}
if (!fileAlreadyInitialized) {
writeHeader(headers);
csvWriter.flush();
}
}
private Writer constructWriter(File csvFile, boolean append, CsvAuditEventHandlerConfiguration config)
throws IOException {
TextWriter textWriter;
if (config.getFileRotation().isRotationEnabled()) {
rotatableWriter = new RotatableWriter(csvFile, config, append);
textWriter = rotatableWriter;
} else {
textWriter = new TextWriter.Stream(new FileOutputStream(csvFile, append));
}
if (config.getBuffering().isEnabled()) {
EventBufferingConfiguration bufferConfig = config.getBuffering();
textWriter = new AsynchronousTextWriter("CsvHandler", bufferConfig.isAutoFlush(), textWriter);
}
return new TextWriterAdapter(textWriter);
}
/**
* Forces rotation of the writer.
* <p>
* Rotation is possible only if file rotation is enabled.
*
* @return {@code true} if rotation was done, {@code false} otherwise.
* @throws IOException
* If an error occurs
*/
@Override
public boolean forceRotation() throws IOException {
return rotatableWriter != null ? rotatableWriter.forceRotation() : false;
}
public void writeHeader(String... headers) throws IOException {
csvWriter.write(csvFormatter.formatHeader(headers));
}
/**
* Write a row into the CSV files.
* @param values The keys of the {@link Map} have to match the column's header.
* @throws IOException
*/
@Override
public void writeEvent(Map<String, String> values) throws IOException {
csvWriter.write(csvFormatter.formatEvent(values, headers));
}
/**
* Flush the data into the CSV file.
* @throws IOException
*/
public void flush() throws IOException {
csvWriter.flush();
}
@Override
public void close() throws IOException {
csvWriter.close();
}
}