001/*
002 * #%L
003 * Netarchivesuite - archive
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023package dk.netarkivet.archive.arcrepository.bitpreservation;
024
025import java.io.File;
026import java.util.Date;
027import java.util.List;
028import java.util.Set;
029
030import dk.netarkivet.archive.ArchiveSettings;
031import dk.netarkivet.common.distribute.arcrepository.Replica;
032import dk.netarkivet.common.exceptions.ArgumentNotValid;
033import dk.netarkivet.common.exceptions.IOFailure;
034import dk.netarkivet.common.exceptions.IllegalState;
035import dk.netarkivet.common.utils.FileUtils;
036import dk.netarkivet.common.utils.Settings;
037
038/**
039 * This class encapsulates access to the files used in bitpreservation.
040 * <p>
041 * The following files are encapsulated:
042 * <p>
043 * "unsorted.txt": Unsorted list of files in a bitarchive "sorted.txt": Sorted list of files in a bitarchive
044 * <p>
045 * "missingba.txt": Files that are missing in a bitarchive "missingadmindata.txt"; Files that are missing from admin
046 * data "wrongfiles.txt": Files with wrong checksum??? "referenceba.txt"; File list from reference ba?
047 * <p>
048 * "wrongstates.txt"; Files that are in wrong state "insertinadmin.txt"; Files to insert into admin data
049 * "deletefromadmin.txt"; Files to delete from admin data "uploadtoba.txt"; Files to upload to the bitarchive
050 * "deletefromba.txt"; Files to delete from the bitarchive
051 */
052public enum WorkFiles {
053    /**
054     * The MISSING_FILES_BA is the workfile for the list of missing files for a bitarchive.
055     */
056    MISSING_FILES_BA,
057    /**
058     * The MISSING_FILES_ADMINDATA is the workfile for the list of missing files for the admin data.
059     */
060    MISSING_FILES_ADMINDATA,
061    /** WRONG_FILES. Files with wrong checksum??? . */
062    WRONG_FILES,
063    /** FILES_ON_REFERENCE_BA. File list from reference ba?. */
064    FILES_ON_REFERENCE_BA,
065    /** INSERT_IN_ADMIN. Files to insert into admin data . */
066    INSERT_IN_ADMIN,
067    /** DELETE_FROM_ADMIN. Files to delete from admin data . */
068    DELETE_FROM_ADMIN,
069    /** UPLOAD_TO_BA. Files to upload to the bitarchive . */
070    UPLOAD_TO_BA,
071    /** DELETE_FROM_BA. Files to delete from the bitarchive . */
072    DELETE_FROM_BA,
073    /** WRONG_STATES. Files that are in wrong state . */
074    WRONG_STATES,
075    /** FILES_ON_BA. Unsorted list of files in a bitarchive . */
076    FILES_ON_BA,
077    /** CHECKSUMS_ON_BA. Unsorted list of files in a bitarchive . */
078    CHECKSUMS_ON_BA;
079
080    /**
081     * Directory and filenames to be used by ActiveBitPreservation when checking for missing files in a bitarchive.
082     */
083    private static final String FILELIST_OUTPUT_DIR = "filelistOutput";
084    /** missingFiles . */
085    private static final String MISSING_FILES_DIR = "missingFiles";
086    /** checksums . */
087    private static final String CHECKSUM_DIR = "checksums";
088    /** wrongFiles . */
089    private static final String WRONGFILESDIR = "wrongFiles";
090    /** referenceFiles . */
091    private static final String BA_LIST_DIR = "referenceFiles";
092    /** actionsList . */
093    private static final String ACTION_LIST_DIR = "actionList";
094
095    /** unsorted.txt . */
096    private static final String FILE_LISTING_FILENAME = "unsorted.txt";
097    /** sorted.txt . */
098    private static final String SORTED_OUTPUT_FILE = "sorted.txt";
099    /** missingba.txt . */
100    private static final String MISSING_FILES_BA_FILENAME = "missingba.txt";
101    /** missingadmindata.txt . */
102    private static final String MISSING_FILES_ADMINDATA_FILENAME = "missingadmindata.txt";
103    /** wrongfiles.txt . */
104    private static final String WRONG_FILES_FILENAME = "wrongfiles.txt";
105    /** insertinadmin.txt . */
106    private static final String INSERT_IN_ADMIN_FILENAME = "insertinadmin.txt";
107    /** deletefromadmin.txt . */
108    private static final String DELETE_FROM_ADMIN_FILENAME = "deletefromadmin.txt";
109    /** uploadtoba.txt . */
110    private static final String UPLOAD_TO_BA_FILENAME = "uploadtoba.txt";
111    /** deletefromba.txt . */
112    private static final String DELETE_FROM_BA_FILENAME = "deletefromba.txt";
113    /** wrongstates.txt . */
114    private static final String WRONG_STATES_FILENAME = "wrongstates.txt";
115
116    /**
117     * Get the name of the file (sans dir) that corresponds to a given type of workfile.
118     *
119     * @param fileType the type of workfile.
120     * @return A string with the filename (sans dir) of the corresponding file.
121     * @throws IllegalState If it is an unsupported file type.
122     */
123    private static String getFileName(WorkFiles fileType) throws IllegalState {
124        // check the filetype
125        switch (fileType) {
126        case MISSING_FILES_BA:
127            return MISSING_FILES_BA_FILENAME;
128        case MISSING_FILES_ADMINDATA:
129            return MISSING_FILES_ADMINDATA_FILENAME;
130        case WRONG_FILES:
131            return WRONG_FILES_FILENAME;
132        case INSERT_IN_ADMIN:
133            return INSERT_IN_ADMIN_FILENAME;
134        case DELETE_FROM_ADMIN:
135            return DELETE_FROM_ADMIN_FILENAME;
136        case UPLOAD_TO_BA:
137            return UPLOAD_TO_BA_FILENAME;
138        case DELETE_FROM_BA:
139            return DELETE_FROM_BA_FILENAME;
140        case WRONG_STATES:
141            return WRONG_STATES_FILENAME;
142        case FILES_ON_REFERENCE_BA:
143        case FILES_ON_BA:
144        case CHECKSUMS_ON_BA:
145            return FILE_LISTING_FILENAME;
146        default:
147            throw new IllegalState("Impossible workfile type " + fileType);
148        }
149    }
150
151    /**
152     * Get the directory that files of a given type should go into.
153     *
154     * @param rep The replica that we're doing bitpreservation for.
155     * @param fileType The type of file we will be using.
156     * @return A directory (created if necessary) under the bitpreservation dir (see settings) for the file to go into.
157     * @throws IllegalState If it is an unsupported file type.
158     */
159    private static File getDir(Replica rep, WorkFiles fileType) throws IllegalState {
160        // check the type
161        switch (fileType) {
162        case MISSING_FILES_BA:
163        case MISSING_FILES_ADMINDATA:
164            return makeRelativeDir(rep, MISSING_FILES_DIR);
165        case WRONG_STATES:
166        case WRONG_FILES:
167            return makeRelativeDir(rep, WRONGFILESDIR);
168        case FILES_ON_BA:
169            return getFilelistOutputDir(makeRelativeDir(rep, MISSING_FILES_DIR));
170        case FILES_ON_REFERENCE_BA:
171            return getFilelistOutputDir(makeRelativeDir(rep, BA_LIST_DIR));
172        case INSERT_IN_ADMIN:
173        case DELETE_FROM_ADMIN:
174        case UPLOAD_TO_BA:
175        case DELETE_FROM_BA:
176            return makeRelativeDir(rep, ACTION_LIST_DIR);
177        case CHECKSUMS_ON_BA:
178            return getFilelistOutputDir(makeRelativeDir(rep, CHECKSUM_DIR));
179        default:
180            throw new IllegalState("Impossible workfile type " + fileType);
181        }
182    }
183
184    /**
185     * Get the directory that file listings are to live in, creating it if necessary.
186     *
187     * @param dir The directory that the file listings should live under. Note that this is not directly derived from
188     * the name of the replica, as it can also be used for reference file listings.
189     * @return The directory to put file listings in under the given dir.
190     */
191    private static File getFilelistOutputDir(File dir) {
192        String filelistOutputDir = FILELIST_OUTPUT_DIR;
193        File outputDir = new File(dir, filelistOutputDir);
194        FileUtils.createDir(outputDir);
195        return outputDir;
196    }
197
198    /**
199     * Get the base dir for all files related to bitpreservation for a given bitarchive.
200     *
201     * @param replica The name of a bitarchive.
202     * @return The directory to place bitpreservation for the archive under.
203     */
204    protected static File getPreservationDir(Replica replica) {
205        File base = new File(Settings.get(ArchiveSettings.DIR_ARCREPOSITORY_BITPRESERVATION));
206        return new File(base, replica.getId());
207    }
208
209    /**
210     * Make a directory object relative to an replica, creating the directory if necessary.
211     *
212     * @param replica The replica that we're doing bitpreservation for
213     * @param dirname The name of the directory to create
214     * @return An object representing the directory
215     */
216    private static File makeRelativeDir(Replica replica, String dirname) {
217        File dir = getPreservationDir(replica);
218        File outputDir = new File(dir, dirname);
219        FileUtils.createDir(outputDir);
220        return outputDir;
221    }
222
223    /**
224     * Get a sorted file from an unsorted one, updating if necessary.
225     *
226     * @param unsortedFile An unsorted file
227     * @return A file that contains the same lines as unsortedFile, but sorted. The file will be placed in the same
228     * directory as the input file, but have the name Constants.SORTED_OUTPUT_FILE defines.
229     * @throws IOFailure If the file does not exist.
230     */
231    protected static File getSortedFile(File unsortedFile) throws IOFailure {
232        unsortedFile = unsortedFile.getAbsoluteFile();
233        File sortedOutput = new File(unsortedFile.getParentFile(), SORTED_OUTPUT_FILE);
234        if (!unsortedFile.exists()) {
235            throw new IOFailure("Unsorted input file '" + unsortedFile + "' does not exist");
236        }
237        if (unsortedFile.lastModified() >= sortedOutput.lastModified()) {
238            FileUtils.makeSortedFile(unsortedFile, sortedOutput);
239        }
240        return sortedOutput;
241    }
242
243    /* public interfaces below here. */
244
245    /**
246     * Method for writing the list of files to a work file.
247     *
248     * @param replica The replica for the working file.
249     * @param fileType The type of working file.
250     * @param files The list of filenames (?) to write to the working file.
251     */
252    public static void write(Replica replica, WorkFiles fileType, Set<String> files) {
253        ArgumentNotValid.checkNotNull(replica, "Replica replica");
254        ArgumentNotValid.checkNotNull(fileType, "WorkFiles fileType");
255        ArgumentNotValid.checkNotNull(files, "Set<String> files");
256        FileUtils.writeCollectionToFile(getFile(replica, fileType), files);
257    }
258
259    /**
260     * Method for retrieving a working file. Note: it is not tested, whether the file exists.
261     *
262     * @param rep The replica to whom the file corresponds.
263     * @param fileType The type of working file.
264     * @return The requested working file.
265     */
266    public static File getFile(Replica rep, WorkFiles fileType) {
267        ArgumentNotValid.checkNotNull(rep, "Replica rep");
268        ArgumentNotValid.checkNotNull(fileType, "WorkFiles fileType");
269        return new File(getDir(rep, fileType), getFileName(fileType));
270    }
271
272    /**
273     * Method for retrieving the last modified date of a working file for a specific replica.
274     * <p>
275     * FIXME this might throw odd exceptions if the file does not exist.
276     *
277     * @param rep The replica for the working file.
278     * @param fileType The type of working file.
279     * @return The last modified date for the working file.
280     */
281    public static Date getLastUpdate(Replica rep, WorkFiles fileType) {
282        ArgumentNotValid.checkNotNull(rep, "Replica rep");
283        ArgumentNotValid.checkNotNull(fileType, "WorkFiles fileType");
284        return new Date(getFile(rep, fileType).lastModified());
285    }
286
287    /**
288     * Method for retrieving the lines of a working file for a specific replica.
289     *
290     * @param replica The replica of the working file.
291     * @param fileType The type of workfile.
292     * @return A list containing the lines of the file.
293     */
294    public static List<String> getLines(Replica replica, WorkFiles fileType) {
295        ArgumentNotValid.checkNotNull(replica, "Replica replica");
296        ArgumentNotValid.checkNotNull(fileType, "WorkFiles fileType");
297        return FileUtils.readListFromFile(getFile(replica, fileType));
298    }
299}