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.sql.Date;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import dk.netarkivet.archive.arcrepositoryadmin.BitPreservationDAO;
037import dk.netarkivet.archive.arcrepositoryadmin.ReplicaCacheDatabase;
038import dk.netarkivet.archive.arcrepositoryadmin.ReplicaFileInfo;
039import dk.netarkivet.common.distribute.arcrepository.ArcRepositoryClientFactory;
040import dk.netarkivet.common.distribute.arcrepository.PreservationArcRepositoryClient;
041import dk.netarkivet.common.distribute.arcrepository.Replica;
042import dk.netarkivet.common.exceptions.ArgumentNotValid;
043import dk.netarkivet.common.exceptions.IOFailure;
044import dk.netarkivet.common.exceptions.NotImplementedException;
045import dk.netarkivet.common.utils.CleanupHook;
046import dk.netarkivet.common.utils.CleanupIF;
047import dk.netarkivet.common.utils.FileUtils;
048
049/**
050 * The database based active bit preservation. This is the alternative to the FileBasedActiveBitPreservation.
051 * <p>
052 * A database is used to handle the bitpreservation.
053 */
054public final class DatabaseBasedActiveBitPreservation implements ActiveBitPreservation, CleanupIF {
055
056    /** The log. */
057    private static final Logger log = LoggerFactory.getLogger(DatabaseBasedActiveBitPreservation.class);
058
059    /**
060     * When replacing a broken file, the broken file is downloaded and stored in a temporary directory under
061     * Settings.COMMON_TEMP_DIR with this name. It can then be inspected at your leisure. TODO this is the same constant
062     * as in FileBasedActiveBitPreservation, thus change to global constant instead. Perhaps constant in parent class.
063     */
064    private static final String REMOVED_FILES = "bitpreservation";
065
066    /** The current instance. */
067    private static DatabaseBasedActiveBitPreservation instance;
068
069    /** Hook to close down this application. */
070    private CleanupHook closeHook;
071
072    /** The instance to contain the access to the database. */
073    private BitPreservationDAO cache;
074
075    /** The list of the replicas, which are having their filelist updated. */
076    private List<Replica> updateFilelistReplicas = Collections.synchronizedList(new ArrayList<Replica>());
077
078    /** The list of the replicas, which are having their checksums updated. */
079    private List<Replica> updateChecksumReplicas = Collections.synchronizedList(new ArrayList<Replica>());
080
081    /**
082     * Constructor. Initialises the database and closeHook.
083     */
084    private DatabaseBasedActiveBitPreservation() {
085        // initialise the database.
086        cache = ReplicaCacheDatabase.getInstance();
087
088        // create and initialise the closing hook
089        closeHook = new CleanupHook(this);
090        Runtime.getRuntime().addShutdownHook(closeHook);
091    }
092
093    /**
094     * Method for retrieving the current instance of this class.
095     *
096     * @return The instance.
097     */
098    public static synchronized DatabaseBasedActiveBitPreservation getInstance() {
099        if (instance == null) {
100            instance = new DatabaseBasedActiveBitPreservation();
101        }
102        return instance;
103    }
104
105    /**
106     * Method for retrieving the filelist from a specific replica. A GetAllFilenamesMessage is sent to the specific
107     * replica.
108     *
109     * @param replica The replica to retrieve the filelist from.
110     * @return The names of the files in a File.
111     * @throws ArgumentNotValid If the replica is 'null'.
112     */
113    private File getFilenamesAsFile(Replica replica) throws ArgumentNotValid {
114        // validate
115        ArgumentNotValid.checkNotNull(replica, "Replica replica");
116
117        // send request
118        log.info("Retrieving filelist from replica '{}'.", replica);
119
120        // Retrieve a file containing the list of filenames of the replica.
121        File result = ArcRepositoryClientFactory.getPreservationInstance().getAllFilenames(replica.getId());
122        log.info("Retrieved filelist from replica '{}'.", replica);
123        return result;
124    }
125
126    /**
127     * Method for retrieving the checksums from a specific replica. A GetAllChecksumsMessage is sent to the specific
128     * replica.
129     *
130     * @param replica The replica to retrieve the checksums from.
131     * @return A file containing the checksumjob results, i.e. a filename##checksum.
132     * @throws ArgumentNotValid If the replica is null.
133     */
134    private File getChecksumListAsFile(Replica replica) throws ArgumentNotValid {
135        // validate
136        ArgumentNotValid.checkNotNull(replica, "Replica replica");
137
138        log.info("Retrieving checksum from replica '{}'.", replica);
139
140        // Request and retrieve a file containing the checksums of the replica,
141        // and return this
142        File outputFile = ArcRepositoryClientFactory.getPreservationInstance().getAllChecksums(replica.getId());
143
144        log.info("Retrieved checksum from replica '{}'.", replica);
145
146        return outputFile;
147    }
148
149    /**
150     * Method to reestablish a file missing in a replica. The file is retrieved from a given bitarchive replica, which
151     * is known to contain a proper version of this file. The reestablishment is done by first retrieving the file and
152     * then sending out a store message with this file. Then any replica who is missing the file will obtain it.
153     *
154     * @param filename The name of the file to reestablish.
155     * @param repWithFile The replica where the file should be retrieved from.
156     * @throws IOFailure If the attempt to reestablish the file fails.
157     */
158    private void reestablishMissingFile(String filename, Replica repWithFile) throws IOFailure {
159        // send a GetFileMessage to this bitarchive replica for the file.
160        try {
161            // Contact the ArcRepository.
162            PreservationArcRepositoryClient arcrep = ArcRepositoryClientFactory.getPreservationInstance();
163            // Create temporary file.
164            File tmpDir = FileUtils.createUniqueTempDir(FileUtils.getTempDir(), REMOVED_FILES);
165            File missingFile = new File(tmpDir, filename);
166            // retrieve the file and send store message with it. Then the
167            // file will be stored in all replicas, where it is missing.
168            arcrep.getFile(filename, repWithFile, missingFile);
169            arcrep.store(missingFile);
170            // remove the temporary file afterwards.
171            tmpDir.delete();
172
173            // Update the checksum status for the file.
174            cache.updateChecksumStatus(filename);
175        } catch (Exception e) {
176            String errMsg = "Failed to reestablish '" + filename + "' with copy from '" + repWithFile + "'";
177            log.warn(errMsg);
178            // log error and report file.
179            throw new IOFailure(errMsg, e);
180        }
181    }
182
183    /**
184     * Method for retrieving a file from a bitarchive (for replacing a bad entry in another replica).
185     *
186     * @param filename The file to retrieve.
187     * @param repWithFile The replica where the file should be retrieved from.
188     * @return The file from the bitarchive.
189     */
190    private File retrieveRemoteFile(String filename, Replica repWithFile) {
191        // Contact the ArcRepository.
192        PreservationArcRepositoryClient arcrep = ArcRepositoryClientFactory.getPreservationInstance();
193        // Create temporary file.
194        File tmpDir = FileUtils.createUniqueTempDir(FileUtils.getTempDir(), REMOVED_FILES);
195        File missingFile = new File(tmpDir, filename);
196        // retrieve the file and send store message with it. Then the
197        // file will be stored in all replicas, where it is missing.
198        arcrep.getFile(filename, repWithFile, missingFile);
199
200        return missingFile;
201    }
202
203    /**
204     * This method is used for making sure, that all replicas are up-to-date before trying to validate the checksums of
205     * the files within it.
206     * <p>
207     * TODO set a time limit for last date to update. This has to be a variable in settings, which should have the
208     * default '0', meaning no time limit. If more time has passed than acceptable, then a new checksum job should be
209     * run. This has to do with assignment B.2.4 - Bitpreservation scheduler.
210     */
211    private void initChecksumStatusUpdate() {
212        // go through all replicas.
213        for (Replica replica : Replica.getKnown()) {
214            // retrieve the date for the last checksum update.
215            Date csDate = cache.getDateOfLastWrongFilesUpdate(replica);
216
217            if (csDate == null) {
218                // run a checksum job on the replica.
219                log.info("Starting checksum update for replica {}", replica);
220                runChecksum(replica);
221                log.info("Finished the checksum update for replica {}", replica);
222            }
223        }
224    }
225
226    /**
227     * The method for retrieving the checksums for all the files within a replica. This method sends the checksum job to
228     * the replica archive.
229     *
230     * @param replica The replica to retrieve the checksums from.
231     */
232    private void runChecksum(Replica replica) {
233        File checksumlistFile = null;
234        try {
235            checksumlistFile = getChecksumListAsFile(replica);
236            cache.addChecksumInformation(checksumlistFile, replica);
237        } finally {
238            if (checksumlistFile != null) {
239                FileUtils.remove(checksumlistFile);
240            }
241        }
242    }
243
244    /**
245     * Retrieves and update the status of a file for a specific replica.
246     *
247     * @param filename The name of the file.
248     */
249    private void updateChecksumStatus(String filename) {
250        // retrieve the ArcRepositoryClient before using it in the for-loop.
251        PreservationArcRepositoryClient arcClient = ArcRepositoryClientFactory.getPreservationInstance();
252
253        // retrieve the checksum status for the file for all the replicas
254        for (Replica replica : Replica.getKnown()) {
255            // retrieve the checksum.
256            String checksum = arcClient.getChecksum(replica.getId(), filename);
257
258            // insert the checksum results for the file into the database.
259            cache.updateChecksumInformationForFileOnReplica(filename, checksum, replica);
260        }
261
262        // Vote for the specific file.
263        cache.updateChecksumStatus(filename);
264    }
265
266    /**
267     * The method calculates the number of files which has a wrong checksum for the replica. This simple counts all the
268     * entries in the replicafileinfo table for the replica where the filelist_status is set to CORRUPT.
269     *
270     * @param replica The replica for which to count the number of changed files.
271     * @return The number of files for the replica where the checksum does not correspond to the checksum of the same
272     * file in the other replicas.
273     * @throws ArgumentNotValid If the replica is null.
274     */
275    public long getNumberOfChangedFiles(Replica replica) throws ArgumentNotValid {
276        // validate
277        ArgumentNotValid.checkNotNull(replica, "Replica replica");
278
279        // Retrieves the number of entries in replicafileinfo which has
280        // filelist_status set to 'CORRUPT' and belong to the replica.
281        return cache.getNumberOfWrongFilesInLastUpdate(replica);
282    }
283
284    /**
285     * This method retrieves the name of all the files which has a wrong checksum for the replica. It simple returns the
286     * filename of all the entries in the replicafileinfo table for the replica where the filelist_status is set to
287     * CORRUPT.
288     *
289     * @param replica The replica for which the changed files should be found.
290     * @return The names of files in the replica where the checksum does not correspond to the checksum of the same file
291     * in the other replicas.
292     * @throws ArgumentNotValid If the replica is null.
293     */
294    public Iterable<String> getChangedFiles(Replica replica) throws ArgumentNotValid {
295        // validate
296        ArgumentNotValid.checkNotNull(replica, "Replica replica");
297
298        // Retrieves the list of filenames for the entries in replicafileinfo
299        // which has filelist_status set to 'CORRUPT' and belong to the replica.
300        return cache.getWrongFilesInLastUpdate(replica);
301    }
302
303    /**
304     * This method calculates the number of files which are not found in the given replica. This simple counts all the
305     * entries in the replicafileinfo table for the replica where the filelist_status is set to MISSING.
306     *
307     * @param replica The replica for which to count the number of missing files.
308     * @return The number of files which is missing in the replica.
309     * @throws ArgumentNotValid If the replica is null.
310     */
311    public long getNumberOfMissingFiles(Replica replica) throws ArgumentNotValid {
312        // validate
313        ArgumentNotValid.checkNotNull(replica, "Replica replica");
314
315        // Retrieves the number of entries in replicafileinfo which has
316        // filelist_status set to 'MISSING' and belong to the replica.
317        return cache.getNumberOfMissingFilesInLastUpdate(replica);
318    }
319
320    /**
321     * This method retrieves the name of all the files which are missing for the given replica. It simple returns the
322     * filename of all the entries in the replicafileinfo table for the replica where the filelist_status is set to
323     * MISSING.
324     *
325     * @param replica The replica for which the missing files should be found.
326     * @return The names of files in the replica which are missing.
327     * @throws ArgumentNotValid If the replica is null.
328     */
329    public Iterable<String> getMissingFiles(Replica replica) throws ArgumentNotValid {
330        // validate
331        ArgumentNotValid.checkNotNull(replica, "Replica replica");
332
333        // Retrieves the list of filenames for the entries in replicafileinfo
334        // which has filelist_status set to 'MISSING' and belong to the replica.
335        return cache.getMissingFilesInLastUpdate(replica);
336    }
337
338    /**
339     * This method retrieves the date for the latest checksum update was performed for the replica. This means the date
340     * for the latest the replica has calculated the checksum of all the files within its archive.
341     * <p>
342     * This method does not call out to the replicas. It only contacts the local database.
343     *
344     * @param replica The replica for which the date for last checksum update should be retrieved.
345     * @return The date for the last time the checksums has been update. If the checksum update has never occurred, then
346     * a null is returned.
347     * @throws ArgumentNotValid If the replica is null.
348     */
349    public Date getDateForChangedFiles(Replica replica) throws ArgumentNotValid {
350        // validate
351        ArgumentNotValid.checkNotNull(replica, "Replica replica");
352
353        // Retrieves the checksum_updated date for the entry in the replica
354        // table in the database corresponding to the replica argument.
355        return cache.getDateOfLastWrongFilesUpdate(replica);
356    }
357
358    /**
359     * This method retrieves the date for the latest filelist update was performed for the replica. This means the date
360     * for the latest the replica has retrieved the list of all the files within the archive.
361     * <p>
362     * This method does not call out to the replicas. It only contacts the local database.
363     *
364     * @param replica The replica for which the date for last filelist update should be retrieved.
365     * @return The date for the last time the filelist has been update. If the filelist update has never occurred, then
366     * a null is returned.
367     * @throws ArgumentNotValid If the replica is null.
368     */
369    public Date getDateForMissingFiles(Replica replica) throws ArgumentNotValid {
370        // validate
371        ArgumentNotValid.checkNotNull(replica, "Replica replica");
372
373        // Retrieves the filelist_updated date for the entry in the replica
374        // table in the database corresponding to the replica argument.
375        return cache.getDateOfLastMissingFilesUpdate(replica);
376    }
377
378    /**
379     * The method is used to update the checksum for all the files in a replica. The checksum for the replica is
380     * retrieved through GetAllChecksumMessages. This will take a very large amount of time for the bitarchive, but a
381     * more limited amount of time for the checksumarchive.
382     * <p>
383     * The corresponding replicafileinfo entries in the database for the retrieved checksum results will be updated.
384     * Then a checksum update will be performed to check for corrupted replicafileinfo.
385     * <p>
386     * Each replica can only be updated once at the time.
387     *
388     * @param replica The replica to find the changed files for.
389     * @throws ArgumentNotValid If the replica is null.
390     */
391    public void findChangedFiles(Replica replica) throws ArgumentNotValid {
392        // validate
393        ArgumentNotValid.checkNotNull(replica, "Replica replica");
394
395        // check whether a update of the replica is already in progress.
396        if (updateChecksumReplicas.contains(replica)) {
397            log.warn("A checksum update is already being performed for replica '{}'. Operation ignored!", replica);
398            return;
399        }
400
401        log.info("Initiating findChangedFiles for replica '{}'.", replica);
402        updateChecksumReplicas.add(replica);
403
404        try {
405            // retrieve updated checksums from the replica.
406            runChecksum(replica);
407
408            // make sure, that all replicas are up-to-date.
409            initChecksumStatusUpdate();
410
411            // update to find changes.
412            cache.updateChecksumStatus();
413            log.info("Completed findChangedFiles for replica '{}'.", replica);
414        } finally {
415            updateChecksumReplicas.remove(replica);
416        }
417    }
418
419    /**
420     * This method retrieves the filelist for the replica, and then it updates the database with this list of filenames.
421     * Each replica can only be updated once at the time.
422     *
423     * @param replica The replica to find the missing files for.
424     * @throws ArgumentNotValid If the replica is null.
425     */
426    public void findMissingFiles(Replica replica) throws ArgumentNotValid {
427        // validate
428        ArgumentNotValid.checkNotNull(replica, "Replica replica");
429
430        // check whether a update of the replica is already in progress.
431        if (updateFilelistReplicas.contains(replica)) {
432            log.warn("A filelist update is already being performed for " + "replica '{}'. Operation ignored!", replica);
433            return;
434        }
435        log.info("Initiating findMissingFiles for replica '{}'.", replica);
436        updateFilelistReplicas.add(replica);
437        File filenamesFile = null;
438        try {
439            // retrieve the filelist from the replica
440            filenamesFile = getFilenamesAsFile(replica);
441            // put them into the database.
442            cache.addFileListInformation(filenamesFile, replica);
443            log.info("Completed findMissingFiles for replica '{}'.", replica);
444        } finally {
445            updateFilelistReplicas.remove(replica);
446            if (filenamesFile != null) {
447                FileUtils.remove(filenamesFile);
448            }
449        }
450    }
451
452    /**
453     * Method for retrieving the FilePreservationState for a specific file.
454     *
455     * @param filename The name of the file for whom the FilePreservationState should be retrieved.
456     * @return The FilePreservationState for the file.
457     * @throws ArgumentNotValid If the filename is null or the empty string.
458     */
459    @Override
460    public PreservationState getPreservationState(String filename) throws ArgumentNotValid {
461        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
462
463        // Initialize the list of replicafileinfos, with one entry per replica.
464        List<ReplicaFileInfo> rfis = new ArrayList<ReplicaFileInfo>(Replica.getKnown().size());
465
466        // update the checksum status for the file for all the replicas.
467        updateChecksumStatus(filename);
468
469        // Retrieve the replicafileinfo entries for the file.
470        for (Replica replica : Replica.getKnown()) {
471            rfis.add(cache.getReplicaFileInfo(filename, replica));
472        }
473
474        // use filename and replicafileinfos to make the resulting
475        // DatabasePreservationState.
476        return new DatabasePreservationState(filename, rfis);
477    }
478
479    /**
480     * Method for retrieving the FilePreservationState for a list of files.
481     *
482     * @param filenames The list of filenames whose FilePreservationState should be retrieved.
483     * @return A mapping between the filenames and their FilePreservationState.
484     * @throws ArgumentNotValid If the list of filenames are null.
485     */
486    @Override
487    public Map<String, PreservationState> getPreservationStateMap(String... filenames) throws ArgumentNotValid {
488        ArgumentNotValid.checkNotNull(filenames, "String... filenames");
489
490        // make the resulting map.
491        Map<String, PreservationState> res = new HashMap<String, PreservationState>();
492
493        // retrieve the preservation states and put them into the map.
494        for (String file : filenames) {
495            res.put(file, getPreservationState(file));
496        }
497
498        return res;
499    }
500
501    /**
502     * This method finds the number of files which are known to be in the archive of a specific replica. This method
503     * will not go out to the replica, but only contact the local database. The number of files in the replica is
504     * retrieved from the database by counting the amount of files in the replicafileinfo table which belong to the
505     * replica and which has the filelist_status set to OK.
506     *
507     * @param replica The replica for which the number of files should be counted.
508     * @return The number of files for a specific replica.
509     * @throws ArgumentNotValid If the replica is null.
510     */
511    public long getNumberOfFiles(Replica replica) throws ArgumentNotValid {
512        // validate
513        ArgumentNotValid.checkNotNull(replica, "Replica replica");
514
515        // returns the amount of files, which is not missing.
516        return cache.getNumberOfFiles(replica);
517    }
518
519    /**
520     * Check that the checksum of the file is indeed different to the value in admin data and reference replica. If so,
521     * remove missing file and upload it from reference replica to this replica.
522     *
523     * @param replica The replica to restore file to
524     * @param filename The name of the file
525     * @param credentials The credentials used to perform this replace operation
526     * @param checksum The known bad checksum. Only a file with this bad checksum is attempted repaired.
527     * @throws ArgumentNotValid If any of the arguments are not valid.
528     */
529    public void replaceChangedFile(Replica replica, String filename, String credentials, String checksum)
530            throws ArgumentNotValid {
531        ArgumentNotValid.checkNotNull(replica, "Replica replica");
532        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
533        ArgumentNotValid.checkNotNullOrEmpty(checksum, "String checksum");
534        ArgumentNotValid.checkNotNullOrEmpty(credentials, "String credentials");
535
536        // find replica
537        Replica repWithFile = cache.getBitarchiveWithGoodFile(filename, replica);
538        // retrieve the file from the replica.
539        File missingFile = retrieveRemoteFile(filename, repWithFile);
540        // upload the file to the replica where it is missing
541        ArcRepositoryClientFactory.getPreservationInstance().correct(replica.getId(), checksum, missingFile,
542                credentials);
543    }
544
545    /**
546     * This method is used to upload missing files to a replica. For each file a good version of this file is found, and
547     * it is reestablished on the replicas where it is missing.
548     *
549     * @param replica The replica where the files are missing.
550     * @param filenames The names of the files which are missing in the given replica.
551     * @throws ArgumentNotValid If the replica or list of filenames is null, or if the list of filenames is empty.
552     * @throws IOFailure If some files could not be established.
553     */
554    public void uploadMissingFiles(Replica replica, String... filenames) throws ArgumentNotValid, IOFailure {
555        ArgumentNotValid.checkNotNull(replica, "Replica replica");
556        ArgumentNotValid.checkNotNull(filenames, "String... filenames");
557        ArgumentNotValid.checkPositive(filenames.length, "Length of argument String... filenames");
558        log.info("UploadMissingFiles initiated of {} filenames", filenames.length);
559        // make record of files, which is not uploaded correct.
560        List<String> filesFailedReestablishment = new ArrayList<String>();
561
562        // For each file in filenames
563        for (String file : filenames) {
564            // retrieve a replica which has the file and the checksum_status
565            // is 'OK'. Though do not allow the replica where the file is
566            // missing to be returned.
567            Replica fileRep = cache.getBitarchiveWithGoodFile(file, replica);
568
569            // make sure, that a replica was found.
570            if (fileRep == null) {
571                // issue warning, report file, and continue to next file.
572                String errMsg = "No bitarchive replica contains a version of "
573                        + "the file with an acceptable checksum.";
574                log.warn(errMsg);
575                filesFailedReestablishment.add(file);
576                continue;
577            }
578
579            try {
580                // reestablish the missing file.
581                reestablishMissingFile(file, fileRep);
582            } catch (IOFailure e) {
583                // if error, then not successfully reestablishment for the file.
584                log.warn("The file '{}' has not been reestablished on replica '{}' with a correct version "
585                        + "from replica '{}'.", file, replica, fileRep, e);
586                filesFailedReestablishment.add(file);
587            }
588        }
589
590        // make warning if not all the files could be reestablished.
591        if (filesFailedReestablishment.size() > 0) {
592            throw new IOFailure("The following " + filesFailedReestablishment.size() + " out of " + filenames.length
593                    + " files could not be reestablished: " + filesFailedReestablishment);
594        }
595        log.info("UploadMissingFiles completed of {} filenames", filenames.length);
596    }
597
598    /**
599     * This should reestablish the state for the file.
600     *
601     * @param filename The name of the file to change the state for.
602     * @throws ArgumentNotValid If the filename is invalid.
603     * @throws NotImplementedException This will not be implemented.
604     */
605    @Override
606    public void changeStateForAdminData(String filename) throws ArgumentNotValid, NotImplementedException {
607        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
608
609        // This function should not deal with admin.data.
610        throw new NotImplementedException("This will not be implemented");
611    }
612
613    /**
614     * Old method, which refers to the checksum replica part of admin data.
615     *
616     * @return Nothing, since it always throws an exception.
617     * @throws NotImplementedException This method will not be implemented.
618     */
619    @Override
620    public Iterable<String> getMissingFilesForAdminData() throws NotImplementedException {
621        // This function should not deal with admin.data.
622        throw new NotImplementedException("Old method, which refers to the " + "checksum replica part of admin data.");
623    }
624
625    /**
626     * Old method, which refers to the checksum replica part of admin data.
627     *
628     * @return Nothing, since it always throws an exception.
629     * @throws NotImplementedException This method will not be implemented.
630     */
631    @Override
632    public Iterable<String> getChangedFilesForAdminData() throws NotImplementedException {
633        // This function should not deal with admin.data.
634        throw new NotImplementedException("Old method, which refers to the " + "checksum replica part of admin data.");
635    }
636
637    /**
638     * Old method, which refers to the checksum replica part of admin data.
639     *
640     * @param filenames The list of filenames which should be added to admin data.
641     * @throws NotImplementedException This method will not be implemented.
642     * @throws ArgumentNotValid If filenames invalid.
643     */
644    @Override
645    public void addMissingFilesToAdminData(String... filenames) throws ArgumentNotValid, NotImplementedException {
646        ArgumentNotValid.checkNotNull(filenames, "String... filenames");
647
648        // This function should not deal with admin.data.
649        throw new NotImplementedException("Old method, which refers to the " + "checksum replica part of admin data.");
650    }
651
652    /**
653     * Method for closing the running instance of this class.
654     */
655    public void close() {
656        if (closeHook != null) {
657            Runtime.getRuntime().removeShutdownHook(closeHook);
658        }
659        cleanup();
660    }
661
662    /**
663     * Method for cleaning up this instance.
664     */
665    @Override
666    public void cleanup() {
667        instance = null;
668        cache.cleanup();
669    }
670
671}