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.checksum.distribute;
024
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import dk.netarkivet.archive.bitarchive.distribute.BatchMessage;
029import dk.netarkivet.archive.bitarchive.distribute.GetFileMessage;
030import dk.netarkivet.archive.bitarchive.distribute.GetMessage;
031import dk.netarkivet.archive.bitarchive.distribute.RemoveAndGetFileMessage;
032import dk.netarkivet.archive.bitarchive.distribute.UploadMessage;
033import dk.netarkivet.archive.distribute.ReplicaClient;
034import dk.netarkivet.common.distribute.ChannelID;
035import dk.netarkivet.common.distribute.Channels;
036import dk.netarkivet.common.distribute.JMSConnection;
037import dk.netarkivet.common.distribute.JMSConnectionFactory;
038import dk.netarkivet.common.distribute.RemoteFile;
039import dk.netarkivet.common.distribute.arcrepository.ReplicaType;
040import dk.netarkivet.common.exceptions.ArgumentNotValid;
041import dk.netarkivet.common.exceptions.IOFailure;
042import dk.netarkivet.common.exceptions.IllegalState;
043import dk.netarkivet.common.utils.batch.FileBatchJob;
044
045/**
046 * Proxy for remote checksum archive. Establishes the jms connection to the remote checksum archive.
047 * <p>
048 * Can be used in combination with any type of ChecksumServerAPI.
049 */
050public class ChecksumClient implements ReplicaClient {
051
052    /** The log. */
053    private static final Logger log = LoggerFactory.getLogger(ChecksumClient.class);
054
055    /** Connection to JMS provider. */
056    private JMSConnection jmsCon;
057
058    /** Connection information. The connection to contact all checksum archives. */
059    private ChannelID theChecksumChannel;
060    /** The name of the replica whose client this is. */
061    private String replicaId;
062
063    /**
064     * The constructor. Cannot be used directly, use getInstance instead.
065     *
066     * @param theCRin The channel for contacting the checksum archive.
067     * @throws IOFailure If there is a problem with the connection.
068     */
069    private ChecksumClient(ChannelID theCRin) throws IOFailure {
070        this.theChecksumChannel = theCRin;
071        replicaId = Channels.retrieveReplicaFromIdentifierChannel(theChecksumChannel.getName()).getId();
072        jmsCon = JMSConnectionFactory.getInstance();
073    }
074
075    /**
076     * The method for invoking an instance of this class.
077     *
078     * @param theCRin The channel for contacting the checksum archive.
079     * @return A new instance.
080     * @throws IOFailure If there is a problem with the connection.
081     * @throws ArgumentNotValid If the checksum replica channel is null.
082     */
083    public static ChecksumClient getInstance(ChannelID theCRin) throws IOFailure, ArgumentNotValid {
084        // validate arguments
085        ArgumentNotValid.checkNotNull(theCRin, "ChannelID theCRin");
086
087        // Create a new instance (no static instance, since it would prevent
088        // multi checksum replica clients).
089        return new ChecksumClient(theCRin);
090    }
091
092    /**
093     * Method for sending correct messages to the replica. This CorrectMessage is used to correct a bad entry in the
094     * archive.
095     *
096     * @param msg The CorrectMessage to send to the replica.
097     * @throws ArgumentNotValid If the CorrectMessage is null.
098     */
099    public void sendCorrectMessage(CorrectMessage msg) throws ArgumentNotValid {
100        ArgumentNotValid.checkNotNull(msg, "CorrectMessage msg");
101
102        // send the message to the archive.
103        jmsCon.resend(msg, theChecksumChannel);
104
105        // log the message.
106        log.debug("Resending CorrectMessage: {}'.", msg.toString());
107    }
108
109    /**
110     * Method for sending a GetAllFilenamesMessage to a checksum archive.
111     *
112     * @param msg The GetAllFilenamesMessage, which will be send through the jms connection to the checksum archive.
113     * @throws ArgumentNotValid If the GetAllFilenamesMessage is null.
114     */
115    public void sendGetAllFilenamesMessage(GetAllFilenamesMessage msg) throws ArgumentNotValid {
116        ArgumentNotValid.checkNotNull(msg, "GetAllFilenamesMessage msg");
117        // send the message to the archive.
118        jmsCon.resend(msg, theChecksumChannel);
119
120        // log message.
121        log.debug("Resending GetAllFilenamesMessage: '{}'.", msg.toString());
122    }
123
124    /**
125     * Method for sending the GetAllChecksumMessage to the ChecksumReplica.
126     *
127     * @param msg The GetAllChecksumMessage, which will be sent through the jms connection to the checksum archive.
128     * @throws ArgumentNotValid If the GetAllChecksumsMessage is null.
129     */
130    public void sendGetAllChecksumsMessage(GetAllChecksumsMessage msg) throws ArgumentNotValid {
131        ArgumentNotValid.checkNotNull(msg, "GetAllChecksumsMessage msg");
132        // send the message to the archive.
133        jmsCon.resend(msg, theChecksumChannel);
134
135        // log message.
136        log.debug("Sending GetAllChecksumMessage: '{}'.", msg.toString());
137    }
138
139    /**
140     * Method for retrieving the checksum of a specific arcfile within the archive.
141     *
142     * @param msg The GetChecksumMessage which will be sent to the checksum archive though the jms connection.
143     */
144    public void sendGetChecksumMessage(GetChecksumMessage msg) {
145        // Validate arguments
146        ArgumentNotValid.checkNotNull(msg, "GetChecksumMessage msg");
147
148        jmsCon.resend(msg, theChecksumChannel);
149
150        // log what we are doing.
151        log.debug("Sending GetChecksumMessage: '{}'.", msg.toString());
152    }
153
154    /**
155     * Method for retrieving the checksum of a specific arcfile within the archive.
156     *
157     * @param replyChannel The channel where the reply should be sent.
158     * @param filename The GetChecksumMessage which has been sent to the checksum archive though the jms connection.
159     * @return The GetChecksumMessage which is sent.
160     * @throws ArgumentNotValid If the reply channel is null or if the filename is either null or the empty string.
161     */
162    public GetChecksumMessage sendGetChecksumMessage(ChannelID replyChannel, String filename) throws ArgumentNotValid {
163        // Validate arguments
164        ArgumentNotValid.checkNotNull(replyChannel, "ChannelID replyChannel");
165        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
166
167        // make GetChecksumMessage for the specific file.
168        GetChecksumMessage msg = new GetChecksumMessage(theChecksumChannel, replyChannel, filename, replicaId);
169        jmsCon.send(msg);
170
171        // log what we are doing.
172        log.debug("Sending GetChecksumMessage: '{}'.", msg.toString());
173
174        return msg;
175    }
176
177    /**
178     * Method for retrieving the type of replica. In this case the replica is a checksum replica.
179     *
180     * @return The type of this replica, in this case Checksum replica.
181     */
182    public ReplicaType getType() {
183        return ReplicaType.CHECKSUM;
184    }
185
186    /**
187     * Method for uploading a file to the archive. This is only uploaded to one of the archives, not all of them. Thus
188     * using the 'any' channel.
189     *
190     * @param rf The file to upload to the archive.
191     * @param precomputedChecksum A precomputed checksum 
192     * @throws ArgumentNotValid If the remote file is null.
193     */
194    public void sendUploadMessage(RemoteFile rf, String precomputedChecksum) throws ArgumentNotValid {
195        // validate arguments.
196        ArgumentNotValid.checkNotNull(rf, "RemoteFile rf");
197
198        // create and send message.
199        UploadMessage up = new UploadMessage(theChecksumChannel, Channels.getTheRepos(), rf);
200        up.setPrecomputedChecksum(precomputedChecksum);
201        jmsCon.send(up);
202
203        // log message
204        log.debug("Sending upload message '{}'.", up.toString());
205    }
206
207    /**
208     * Method for sending batch job messages to the replica. This is not allowed since this archive at the end of this
209     * client is a checksum archive, which cannot handle batch jobs.
210     *
211     * @param replyChannel The channel where the reply should be sent.
212     * @param job The batchjob to execute.
213     * @return Nothing. It always throws an exception, since it is not allowed to run batchjobs on a checksum archive.
214     * @throws IllegalState Always. Since it is not legal to send a batchjob to a checksum replica.
215     * @throws ArgumentNotValid If the channel or the batchjob is null.
216     */
217    public BatchMessage sendBatchJob(ChannelID replyChannel, FileBatchJob job) throws IllegalState, ArgumentNotValid {
218        ArgumentNotValid.checkNotNull(replyChannel, "ChannelID replyChannel");
219        ArgumentNotValid.checkNotNull(job, "FileBatchJob job");
220
221        String errMsg = "Trying to execute the batchjob '" + job.getClass().getName() + "'"
222                + " on a checksum replica with reply " + "channel '" + replyChannel + "'. This is not allowed!";
223        log.error(errMsg);
224        throw new IllegalState(errMsg);
225    }
226
227    /**
228     * Method for sending batch job messages to the replica. This is not allowed since this archive at the end of this
229     * client is a checksum archive, which cannot handle batch jobs.
230     *
231     * @param msg The batch message.
232     * @return Nothing. It always throws an exception, since it is not allowed to run batchjobs on a checksum archive.
233     * @throws IllegalState Always. Since it is not legal to send a batchjob to a checksum replica.
234     * @throws ArgumentNotValid If the message is null.
235     */
236    public BatchMessage sendBatchJob(BatchMessage msg) throws IllegalState, ArgumentNotValid {
237        ArgumentNotValid.checkNotNull(msg, "BatchMessage msg");
238
239        String errMsg = "Trying to execute the batchjob '" + msg.toString() + "' on a checksum replica.";
240        log.error(errMsg);
241        throw new IllegalState(errMsg);
242    }
243
244    /**
245     * This method is intended to retrieve a record from an arc-file within the archive. But since this handles checksum
246     * archive, it does not have the actual arc-files, and this function should therefore fail.
247     *
248     * @param msg The GetMessage for retrieving the arc-record from the archive.
249     * @throws IllegalState Always. Since checksum replicas cannot handle this kind of messages.
250     * @throws ArgumentNotValid If the message is null.
251     */
252    public void sendGetMessage(GetMessage msg) throws IllegalState, ArgumentNotValid {
253        ArgumentNotValid.checkNotNull(msg, "GetMessage msg");
254
255        String errMsg = "A checksum replica cannot handle a GetMessage such as '" + msg + "'";
256        log.error(errMsg);
257        throw new IllegalState(errMsg);
258    }
259
260    /**
261     * This method is intended to retrieve an arc-file from the archive. But since this handles checksum archive, it
262     * does not have the actual arc-files, and this function should therefore fail.
263     *
264     * @param gfm The GetFileMessage for retrieving the arc-file from the archive.
265     * @throws IllegalState Always. Since checksum replicas cannot handle this kind of messages.
266     * @throws ArgumentNotValid If the message is null.
267     */
268    public void sendGetFileMessage(GetFileMessage gfm) throws IllegalState, ArgumentNotValid {
269        ArgumentNotValid.checkNotNull(gfm, "GetFileMessage gfm");
270
271        String errMsg = "A checksum replica cannot handle a GetFileMessage such as '" + gfm + "'.";
272        log.error(errMsg);
273        throw new IllegalState(errMsg);
274    }
275
276    /**
277     * This method is intended to retrieve an arc-file from the archive. But since this handles checksum archive, it
278     * does not have the actual arc-files, and this function should therefore fail.
279     *
280     * @param msg The RemoveAndGetFileMessage for removing and retrieving an arc-file from the archive.
281     * @throws IllegalState Always. Since checksum replicas cannot handle this kind of messages.
282     * @throws ArgumentNotValid If the message is null.
283     */
284    public void sendRemoveAndGetFileMessage(RemoveAndGetFileMessage msg) throws IllegalState, ArgumentNotValid {
285        ArgumentNotValid.checkNotNull(msg, "RemoveAndGetFileMessage msg");
286
287        String errMsg = "A checksum replica cannot handle a RemoveAndGetFileMessage such as '" + msg + "'.";
288        log.error(errMsg);
289        throw new IllegalState(errMsg);
290    }
291
292    /**
293     * Method for closing this instance.
294     */
295    public void close() {
296        log.debug("The ChecksumClient for replica '{}' has been shut down.", replicaId);
297    }
298
299}