View Javadoc

1   /*
2    * #%L
3    * Bitrepository Audit Trail Service
4    * %%
5    * Copyright (C) 2010 - 2012 The State and University Library, The Royal Library and The State Archives, Denmark
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 2.1 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-2.1.html>.
20   * #L%
21   */
22  package org.bitrepository.audittrails.preserver;
23  
24  import java.io.File;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.PrintWriter;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.bitrepository.audittrails.store.AuditEventIterator;
34  import org.bitrepository.audittrails.store.AuditTrailStore;
35  import org.bitrepository.bitrepositoryelements.AuditTrailEvent;
36  import org.bitrepository.common.utils.FileUtils;
37  import org.bitrepository.common.utils.SettingsUtils;
38  import org.bitrepository.settings.referencesettings.AuditTrailPreservation;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  /**
43   * Performs the extraction and packaging of audit trails for preservation for a specific collection.
44   * Only packs the audit trails with a larger sequence number than the reached sequence number for the given 
45   * contributor.
46   */
47  public class AuditPacker {
48      /** The log.*/
49      private Logger log = LoggerFactory.getLogger(getClass());
50      /** The audit trail store.*/
51      private final AuditTrailStore store;
52      /** The id of the collection to pack audits for.*/
53      private final String collectionId;
54      /** The list of contributors for this collection. */
55      private final List<String> contributors = new ArrayList<String>();
56      /** The directory where the temporary files are stored.*/
57      private final File directory;
58      /** Map between the contributor id and the reached preservation sequence number. */
59      private Map<String, Long> seqReached = new HashMap<String, Long>();
60          
61      /** Whether the output stream should be appended to the file.*/
62      private static final boolean APPEND = true;
63      
64      /**
65       * Constructor.
66       * @param store The audit trail store
67       */
68      public AuditPacker(AuditTrailStore store, AuditTrailPreservation settings, String collectionId) {
69          this.store = store;
70          this.collectionId = collectionId;
71          this.directory = FileUtils.retrieveDirectory(settings.getAuditTrailPreservationTemporaryDirectory());
72          this.contributors.addAll(SettingsUtils.getAuditContributorsForCollection(collectionId));
73          
74          initialiseReachedSequenceNumbers();
75      }
76      
77      /**
78       * Makes a copy of the map, which is returned.
79       * @return A mapping between the contributor ids and their preservation sequence numbers.
80       */
81      public Map<String, Long> getSequenceNumbersReached() {
82          return new HashMap<String, Long>(seqReached);
83      }
84      
85      /**
86       * Retrieves the preservation sequence number for each contributor and inserts it into the map.
87       */
88      private void initialiseReachedSequenceNumbers() {
89          for(String contributor : contributors) {
90              Long seq = store.getPreservationSequenceNumber(contributor, collectionId);
91              seqReached.put(contributor, seq);
92          }
93      }
94      
95      /**
96       * Creates a new package with all the newest audit trails from all contributors, e.g. all the audit trails with a 
97       * larger sequence number than the reached.
98       * Cleans up the temporary container afterwards.
99       * @return A compressed file with all the audit trails. 
100      */
101     public synchronized File createNewPackage() {
102         File container = new File(directory, collectionId + "-audit-trails-" + System.currentTimeMillis());
103         try {
104             container.createNewFile();
105             packContributors(container);
106             return createCompressedFile(container);
107         } catch (IOException e) {
108             throw new IllegalStateException("Cannot package the newest audit trails.", e);
109         } finally {
110             // cleaning up.
111             if(container.exists()) {
112                 FileUtils.delete(container);
113             }
114         }
115     }
116     
117     /**
118      * Packs all newest audit trails from every contributor into the given file.
119      * @param container The file where the audit trails should be written.
120      * @throws IOException If writing to the file somehow fails.
121      */
122     private void packContributors(File container) throws IOException {
123         PrintWriter writer = null;
124         try {
125             writer = new PrintWriter(new FileOutputStream(container, APPEND));
126             for(String contributor : contributors) {
127                 packContributor(contributor, writer);
128             }
129         } finally {
130             if(writer != null) {
131                 writer.flush();
132                 writer.close();
133             }
134         }
135     }
136     
137     /**
138      * Writes all the newest audit trails for a single contributor to the PrintWriter.
139      * @param contributorId The id of the contributor to write the files for.
140      * @param writer The PrinterWriter where the output will be written.
141      * @return The sequence number reached +1 (to tell which sequence number is next).
142      */
143     private void packContributor(String contributorId, PrintWriter writer) {
144         long nextSeqNumber = store.getPreservationSequenceNumber(contributorId, collectionId);
145         long largestSeqNumber = -1;
146         long numPackedAudits = 0;
147         log.debug("Starting to pack audittrails for contributor: " + contributorId + " for collection: " + collectionId);
148         AuditEventIterator iterator = store.getAuditTrailsByIterator(null, collectionId, contributorId, nextSeqNumber, 
149                 null, null, null, null, null, null, null);
150         Long timeStart = System.currentTimeMillis();
151         long logInterval = 1000;
152 
153         AuditTrailEvent event;
154         log.debug("AuditEventIterator created");
155         while((event = iterator.getNextAuditTrailEvent()) != null) {
156             numPackedAudits++;
157             if(largestSeqNumber < event.getSequenceNumber().longValue()) {
158                 largestSeqNumber = event.getSequenceNumber().longValue();
159             }
160             writer.println(event.toString());
161             
162             if((numPackedAudits % logInterval) == 0) {
163                 log.debug("Packed " + numPackedAudits + " audittrails in: " + (System.currentTimeMillis() - timeStart) + " ms");
164             }
165         }
166         log.debug("Packed a total of: " + numPackedAudits + " audittrails in: " + (System.currentTimeMillis() - timeStart) + " ms");
167         if(numPackedAudits > 0) {
168             seqReached.put(contributorId, largestSeqNumber);
169         }
170     }
171     
172     /**
173      * Compresses the given file into a zip-file.
174      * It will have the same name as the file to compress, just with the extension '.zip', and it will
175      * be placed in the directory defined in settings.
176      * 
177      * @param fileToCompress The file to compress.
178      * @return The compressed file.
179      * @throws IOException If anything goes wrong.
180      */
181     private File createCompressedFile(File fileToCompress) throws IOException {
182         File zippedFile = new File(directory, fileToCompress.getName() + ".zip");
183         FileUtils.zipFile(fileToCompress, zippedFile);
184         return zippedFile;
185     }
186 }