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.arcrepositoryadmin;
024
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.Map;
028import java.util.Set;
029
030import dk.netarkivet.archive.arcrepository.distribute.StoreMessage;
031import dk.netarkivet.common.distribute.Channels;
032import dk.netarkivet.common.distribute.arcrepository.Replica;
033import dk.netarkivet.common.distribute.arcrepository.ReplicaStoreState;
034import dk.netarkivet.common.exceptions.ArgumentNotValid;
035import dk.netarkivet.common.exceptions.IllegalState;
036import dk.netarkivet.common.exceptions.UnknownID;
037
038/**
039 * The administrator class for the ArcRepository when dealing with an database instead of a file (alternative to
040 * AdminData).
041 */
042public final class DatabaseAdmin implements Admin {
043
044    /** The access interface to the administration database. */
045    private ReplicaCacheDatabase database;
046    /** The current instance of this class, to avoid multiple instantiations. */
047    private static DatabaseAdmin instance;
048    /** Administration of store messages. */
049    private Map<String, StoreMessage> storeEntries = new HashMap<String, StoreMessage>();
050
051    /**
052     * Constructor. Initialises the access to the database.
053     */
054    private DatabaseAdmin() {
055        database = ReplicaCacheDatabase.getInstance();
056    }
057
058    /**
059     * Retrieval of a singleton DatabaseAdmin. Ensures that this class is not instantiated multiple times.
060     *
061     * @return The current instance of this class.
062     */
063    public static synchronized DatabaseAdmin getInstance() {
064        if (instance == null) {
065            instance = new DatabaseAdmin();
066        }
067        return instance;
068    }
069
070    /**
071     * Method for adding an entry for administration.
072     *
073     * @param filename The name of the file to be stored.
074     * @param msg The StoreMessage of the entry.
075     * @param checksum The checksum of the entry.
076     * @throws ArgumentNotValid If either the filename or checksum is either null or the empty string.
077     */
078    @Override
079    public void addEntry(String filename, StoreMessage msg, String checksum) throws ArgumentNotValid {
080        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
081        ArgumentNotValid.checkNotNullOrEmpty(checksum, "String checksum");
082
083        // insert this into the entries map.
084        storeEntries.put(filename, msg);
085
086        // insert into database.
087        database.insertNewFileForUpload(filename, checksum);
088    }
089
090    /**
091     * Method for telling whether a file entry exists.
092     *
093     * @param filename The name of the file, the existence of whose entry is to be determined.
094     * @return Whether the entry exists.
095     * @throws ArgumentNotValid If the filename is either null or empty.
096     */
097    @Override
098    public boolean hasEntry(String filename) throws ArgumentNotValid {
099        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
100
101        // See if the file can be found in the database.
102        return database.existsFileInDB(filename);
103    }
104
105    /**
106     * Returns the ReplicaStoreState of a given file in a specific replica.
107     *
108     * @param filename The name of the file for the ReplicaStoreState.
109     * @param replicaChannelName The name of the identification channel for uniquely identifying the replica of for the
110     * ReplicaStoreState.
111     * @return The ReplicaStoreState of a given file in a specific replica.
112     * @throws ArgumentNotValid If the filename or the replica id is null or the empty string.
113     */
114    @Override
115    public ReplicaStoreState getState(String filename, String replicaChannelName) throws ArgumentNotValid {
116        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
117        ArgumentNotValid.checkNotNullOrEmpty(replicaChannelName, "String replicaChannelName");
118
119        Replica rep = Channels.retrieveReplicaFromIdentifierChannel(replicaChannelName);
120
121        // retrieve the ReplicaStoreState from the database.
122        return database.getReplicaStoreState(filename, rep.getId());
123    }
124
125    /**
126     * Determines whether a given file in a specific replica has a valid replica store state. By valid means a replica
127     * store state other that UNKNOWN_UPLOAD_STATE.
128     * <p>
129     * TODO Find out if the assumption that all upload states besides UNKNOWN_UPLOAD_STATE are acceptable!
130     *
131     * @param filename The name of the file for the ReplicaStoreState.
132     * @param repChannelId The identification channel of the replica for the ReplicaStoreState.
133     * @return Whether a given file in a specific replica has a valid store state.
134     * @throws ArgumentNotValid If either the filenames or the replica identification channel is null or the empty
135     * string.
136     */
137    @Override
138    public boolean hasState(String filename, String repChannelId) throws ArgumentNotValid {
139        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
140        ArgumentNotValid.checkNotNullOrEmpty(repChannelId, "String repChannelId");
141
142        // retrieve the replica
143        Replica rep = Channels.retrieveReplicaFromIdentifierChannel(repChannelId);
144
145        // retrieve the state for the entry for the replica and filename
146        ReplicaStoreState state = database.getReplicaStoreState(filename, rep.getId());
147
148        // return whether the entry has a known upload state
149        return state != ReplicaStoreState.UNKNOWN_UPLOAD_STATE;
150    }
151
152    /**
153     * Sets the store state of an entry to a specific value.
154     *
155     * @param filename The name of the file for the entry.
156     * @param repChannelId The identification channel of the replica for the entry.
157     * @param state The new state for the entry.
158     * @throws ArgumentNotValid If the ReplicaStoreState is null, or if either the filename or the replica
159     * identification channel is either null or the empty string.
160     */
161    @Override
162    public void setState(String filename, String repChannelId, ReplicaStoreState state) throws ArgumentNotValid {
163        ArgumentNotValid.checkNotNull(state, "ReplicaStoreState state");
164        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
165        ArgumentNotValid.checkNotNullOrEmpty(repChannelId, "String repChannelId");
166
167        // retrieve the replica
168        Replica rep = Channels.retrieveReplicaFromIdentifierChannel(repChannelId);
169
170        // update the database.
171        database.setReplicaStoreState(filename, rep.getId(), state);
172    }
173
174    /**
175     * Determines whether the StoreMessage of a given file exists.
176     *
177     * @param filename The name of the file to which the existence of the StoreMessage should be determined.
178     * @return Whether the StoreMessage of the file exists.
179     * @throws ArgumentNotValid If the filename is null or the empty string.
180     */
181    @Override
182    public boolean hasReplyInfo(String filename) throws ArgumentNotValid {
183        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
184
185        // check if a entry for the file can be found in the map.
186        return storeEntries.containsKey(filename);
187    }
188
189    /**
190     * Retrieves the StoreMessage of a specific file.
191     *
192     * @param filename The name of the file whose StoreMessage should be retrieved.
193     * @return The StoreMessage corresponding to the file. A null is returned if the corresponding StoreMessage is not
194     * found.
195     * @throws ArgumentNotValid If the filename is either null or the empty string.
196     */
197    @Override
198    public StoreMessage removeReplyInfo(String filename) throws ArgumentNotValid {
199        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
200
201        // extract the entry from the
202        return storeEntries.remove(filename);
203    }
204
205    /**
206     * Assign a StoreMessage to a specific filename. If the filename is already associated with a StoreMessage, then
207     * this StoreMessage will be overwritten by the new StoreMessage.
208     *
209     * @param filename The name of the file to have a StoreMessage assigned.
210     * @param msg The StoreMessage to be assigned to a file.
211     * @throws ArgumentNotValid If the StoreMessage is null or if the filename is either null or the empty string.
212     */
213    @Override
214    public void setReplyInfo(String filename, StoreMessage msg) throws ArgumentNotValid {
215        ArgumentNotValid.checkNotNull(msg, "StoreMessage msg");
216        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
217
218        // put into the map, and overwrite any existing mapping.
219        storeEntries.put(filename, msg);
220    }
221
222    /**
223     * Retrieves the checksum of a given file.
224     *
225     * @param filename The name of the file, whose checksum should be retrieved.
226     * @return The checksum of the file.
227     * @throws ArgumentNotValid If the filename is either null or the empty string.
228     */
229    @Override
230    public String getCheckSum(String filename) throws ArgumentNotValid {
231        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
232
233        // Ensure that we have the file requested.
234        if (!hasEntry(filename)) {
235            throw new UnknownID("Don't know anything about file '" + filename + "'");
236        }
237
238        // Retrieve the checksum for a specific entry.
239        return database.getChecksum(filename);
240    }
241
242    /**
243     * Sets the checksum of a given file.
244     * <p>
245     * It should not be possible to change the checksum in the database through arcrepository.
246     *
247     * @param filename The name of the file to have the checksum changed.
248     * @param checksum The new checksum for the file.
249     * @throws ArgumentNotValid If either the filename or the checksum is either null or the empty string.
250     * @throws IllegalState Always, since it is not allowed for arcrepository to change the checksum of a completed
251     * upload.
252     */
253    @Override
254    public void setCheckSum(String filename, String checksum) throws ArgumentNotValid, IllegalState {
255        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
256        ArgumentNotValid.checkNotNullOrEmpty(checksum, "String checksum");
257
258        // This will not be implemented.
259        throw new IllegalState("It is not possible to change the checksum of a "
260                + " file in the database! Only the checksum of a specific " + "replicafileinfo.");
261    }
262
263    /**
264     * Retrieves a set of the names for all the known files.
265     *
266     * @return A set of the names for all the known file.
267     */
268    @Override
269    public Set<String> getAllFileNames() {
270        // initialise the set
271        Set<String> res = new HashSet<String>();
272        // put the collection of filenames into the set.
273        res.addAll(database.retrieveAllFilenames());
274        // return the set.
275        return res;
276    }
277
278    /**
279     * Retrieves a set with the name of the files with a specific ReplicaStoreState in a specific replica.
280     *
281     * @param rep The replica where the files belong.
282     * @param state The ReplicaStoreState for the files.
283     * @return A set with the names of the files with a specific ReplicaStoreState in a specific replica.
284     * @throws ArgumentNotValid If the Replica or the ReplicaStoreState is null.
285     */
286    @Override
287    public Set<String> getAllFileNames(Replica rep, ReplicaStoreState state) throws ArgumentNotValid {
288        ArgumentNotValid.checkNotNull(rep, "Replica rep");
289        ArgumentNotValid.checkNotNull(state, "ReplicaStoreState state");
290
291        // initialise the set
292        Set<String> res = new HashSet<String>();
293        // put the collection of filenames into the set.
294        res.addAll(database.retrieveFilenamesForReplicaEntries(rep.getId(), state));
295        // return the set.
296        return res;
297    }
298
299    /**
300     * Close and cleanup of this class.
301     */
302    @Override
303    public void close() {
304        storeEntries.clear();
305        database.cleanup();
306        instance = null;
307    }
308}