1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.forgerock.audit.handlers.csv;
17
18 import static org.forgerock.util.Reject.checkNotNull;
19
20 import java.io.BufferedReader;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.io.Writer;
26 import java.util.Map;
27
28 import org.forgerock.audit.events.handlers.writers.AsynchronousTextWriter;
29 import org.forgerock.audit.events.handlers.writers.RotatableWriter;
30 import org.forgerock.audit.events.handlers.writers.TextWriter;
31 import org.forgerock.audit.events.handlers.writers.TextWriterAdapter;
32 import org.forgerock.audit.handlers.csv.CsvAuditEventHandlerConfiguration.EventBufferingConfiguration;
33 import org.forgerock.util.Reject;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.supercsv.io.CsvMapReader;
37 import org.supercsv.io.ICsvMapReader;
38 import org.supercsv.prefs.CsvPreference;
39
40
41
42
43 class StandardCsvWriter implements CsvWriter {
44
45 private static final Logger logger = LoggerFactory.getLogger(StandardCsvWriter.class);
46
47 private final CsvFormatter csvFormatter;
48 private final String[] headers;
49 private final Writer csvWriter;
50 private RotatableWriter rotatableWriter;
51
52 StandardCsvWriter(File csvFile, String[] headers, CsvPreference csvPreference,
53 CsvAuditEventHandlerConfiguration config) throws IOException {
54 Reject.ifTrue(config.getSecurity().isEnabled(), "StandardCsvWriter should not be used if security is enabled");
55 boolean fileAlreadyInitialized = csvFile.exists();
56 if (fileAlreadyInitialized) {
57 try (ICsvMapReader reader = new CsvMapReader(new BufferedReader(new FileReader(csvFile)), csvPreference)) {
58 final String[] actualHeaders = reader.getHeader(true);
59
60 if (actualHeaders == null) {
61 fileAlreadyInitialized = false;
62 } else {
63 if (actualHeaders.length != headers.length) {
64 throw new IOException("Resuming an existing CSV file but the headers do not match.");
65 }
66 for (int idx = 0; idx < actualHeaders.length; idx++) {
67 if (!actualHeaders[idx].equals(headers[idx])) {
68 throw new IOException("Resuming an existing CSV file but the headers do not match.");
69 }
70 }
71 }
72 }
73 }
74 this.headers = checkNotNull(headers, "The headers can't be null.");
75 csvFormatter = new CsvFormatter(csvPreference);
76 csvWriter = constructWriter(csvFile, fileAlreadyInitialized, config);
77
78 if (rotatableWriter != null) {
79 rotatableWriter.registerRotationHooks(new CsvRotationHooks(csvFormatter, headers));
80 }
81
82 if (!fileAlreadyInitialized) {
83 writeHeader(headers);
84 csvWriter.flush();
85 }
86 }
87
88 private Writer constructWriter(File csvFile, boolean append, CsvAuditEventHandlerConfiguration config)
89 throws IOException {
90 TextWriter textWriter;
91 if (config.getFileRotation().isRotationEnabled()) {
92 rotatableWriter = new RotatableWriter(csvFile, config, append);
93 textWriter = rotatableWriter;
94 } else {
95 textWriter = new TextWriter.Stream(new FileOutputStream(csvFile, append));
96 }
97
98 if (config.getBuffering().isEnabled()) {
99 EventBufferingConfiguration bufferConfig = config.getBuffering();
100 textWriter = new AsynchronousTextWriter("CsvHandler", bufferConfig.isAutoFlush(), textWriter);
101 }
102 return new TextWriterAdapter(textWriter);
103 }
104
105
106
107
108
109
110
111
112
113
114 @Override
115 public boolean forceRotation() throws IOException {
116 return rotatableWriter != null ? rotatableWriter.forceRotation() : false;
117 }
118
119 public void writeHeader(String... headers) throws IOException {
120 csvWriter.write(csvFormatter.formatHeader(headers));
121 }
122
123
124
125
126
127
128 @Override
129 public void writeEvent(Map<String, String> values) throws IOException {
130 csvWriter.write(csvFormatter.formatEvent(values, headers));
131 }
132
133
134
135
136
137 public void flush() throws IOException {
138 csvWriter.flush();
139 }
140
141 @Override
142 public void close() throws IOException {
143 csvWriter.close();
144 }
145
146 }