001/*
002 * #%L
003 * Netarchivesuite - common
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 */
023
024package dk.netarkivet.common.distribute.arcrepository;
025
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033
034import dk.netarkivet.common.CommonSettings;
035import dk.netarkivet.common.distribute.ChannelID;
036import dk.netarkivet.common.distribute.Channels;
037import dk.netarkivet.common.exceptions.ArgumentNotValid;
038import dk.netarkivet.common.exceptions.UnknownID;
039import dk.netarkivet.common.utils.Settings;
040import dk.netarkivet.common.utils.StringTree;
041
042/**
043 * This class encapsulates the bitarchive or checksum replicas. It guarantees that there is only one Replica object per
044 * replica id/name.
045 */
046public class Replica {
047    /** The id of this replica. */
048    private final String id;
049    /** The name of this replica. */
050    private final String name;
051    /** The type of this replica (checksum or bitarchive). */
052    private final ReplicaType type;
053    /**
054     * List of the replicas we know of. This list is initialized by the "first" call to initializeKnownReplicasList().
055     */
056    private static Map<String, Replica> knownReplicas;
057
058    /**
059     * Private constructor that makes a new Replica object. These will all be stored in the knownReplicas map.
060     *
061     * @param repId Id of the replica (e.g. One)
062     * @param repName Name of the replica (e.g. ReplicaOne)
063     * @param repType Type of the replica (e.g. biarchive)
064     */
065    private Replica(String repId, String repName, ReplicaType repType) {
066        this.id = repId;
067        this.name = repName;
068        this.type = repType;
069    }
070
071    /**
072     * Initialize the list of known replicas from settings. This must be called before using known, but after settings
073     * are loaded.
074     */
075    private static synchronized void initializeKnownReplicasList() {
076        if (knownReplicas == null) {
077            String[] replicaIds = Settings.getAll(CommonSettings.REPLICA_IDS);
078            knownReplicas = new HashMap<String, Replica>(replicaIds.length);
079            StringTree<String> replicas = Settings.getTree(CommonSettings.REPLICAS_SETTINGS);
080            List<StringTree<String>> replicaList = replicas.getSubTrees(CommonSettings.REPLICA_TAG);
081            for (StringTree<String> replicaTree : replicaList) {
082                String replicaId = replicaTree.getValue(CommonSettings.REPLICAID_TAG);
083                knownReplicas.put(
084                        replicaId,
085                        new Replica(replicaId, replicaTree.getValue(CommonSettings.REPLICANAME_TAG), ReplicaType
086                                .fromSetting(replicaTree.getValue(CommonSettings.REPLICATYPE_TAG))));
087            }
088        }
089    }
090
091    /**
092     * Get an object representing the replica with the given id.
093     *
094     * @param id The given name of an replica
095     * @return an object representing the replica with the given id
096     * @throws UnknownID if no replica is known with the given id
097     */
098    public static Replica getReplicaFromId(String id) {
099        ArgumentNotValid.checkNotNullOrEmpty(id, "String id");
100        initializeKnownReplicasList();
101        if (!knownReplicas.containsKey(id)) {
102            throw new UnknownID("Can't find replica with id '" + id + "', only know of " + knownReplicas.keySet());
103        }
104        return knownReplicas.get(id);
105    }
106
107    /**
108     * Get an object representing the replica with the given name.
109     *
110     * @param name The given name of an replica
111     * @return an object representing the replica with the given name
112     * @throws UnknownID if no replica is known with the given name
113     */
114    public static Replica getReplicaFromName(String name) {
115        ArgumentNotValid.checkNotNullOrEmpty(name, "String name");
116        initializeKnownReplicasList();
117        // Note that this null value will never be returned.
118        // will always be replaced by non-null value OR the method
119        // will throw an UnknownID exception.
120        Replica resRep = null;
121        boolean found = false;
122        for (Replica rep : knownReplicas.values()) {
123            found = rep.getName().equals(name);
124            if (found) {
125                resRep = rep;
126                break;
127            }
128        }
129        if (!found) {
130            throw new UnknownID("Can't find replica with name '" + name + "', only know of names for " +
131                                knownReplicas.keySet());
132        }
133        return resRep;
134    }
135
136    /**
137     * Check, if a given name is a replica name.
138     *
139     * @param name a given name
140     * @return true, if the given name is a replica name, false otherwise
141     */
142    public static boolean isKnownReplicaName(String name) {
143        ArgumentNotValid.checkNotNullOrEmpty(name, "String name");
144        initializeKnownReplicasList();
145        boolean found = false;
146        for (String s : knownReplicas.keySet()) {
147            found = knownReplicas.get(s).getName().equals(name);
148            if (found) {
149                break;
150            }
151        }
152        return found;
153    }
154
155    /**
156     * Check, if a given id is a replica id.
157     *
158     * @param id a given id
159     * @return true, if the given id is a replica id, false otherwise
160     */
161    public static boolean isKnownReplicaId(String id) {
162        ArgumentNotValid.checkNotNullOrEmpty(id, "String id");
163        initializeKnownReplicasList();
164        boolean found = false;
165        for (String s : knownReplicas.keySet()) {
166            found = s.equals(id);
167            if (found) {
168                break;
169            }
170        }
171        return found;
172    }
173
174    /**
175     * Get all known replicas.
176     *
177     * @return A unmodifiable view of the currently known replicas.
178     */
179    public static Collection<Replica> getKnown() {
180        initializeKnownReplicasList();
181        return Collections.unmodifiableCollection(knownReplicas.values());
182    }
183
184    /**
185     * Get all known replicas as ids.
186     *
187     * @return all known replicas as ids
188     */
189    public static String[] getKnownIds() {
190        initializeKnownReplicasList();
191        String[] knownIds = new String[knownReplicas.keySet().size()];
192        int index = 0;
193        for (String s : knownReplicas.keySet()) {
194            knownIds[index] = s;
195            index++;
196        }
197        return knownIds;
198    }
199
200    /**
201     * Get the id of all known replicas as a string set.
202     *
203     * @return The id of all known replicas as a string set.
204     */
205    public static Set<String> getKnownIdsAsSet() {
206        initializeKnownReplicasList();
207        return knownReplicas.keySet();
208    }
209
210    /**
211     * Get names of all known replicas as string array.
212     *
213     * @return The names of all known replicas as a string array.
214     */
215    public static String[] getKnownNames() {
216        initializeKnownReplicasList();
217        String[] knownNames = new String[knownReplicas.keySet().size()];
218        int index = 0;
219        for (String s : knownReplicas.keySet()) {
220            knownNames[index] = knownReplicas.get(s).getName();
221            ++index;
222        }
223        return knownNames;
224    }
225
226    /**
227     * Get the names of all known replicas as a string set.
228     *
229     * @return The names of all known replicas as a string set.
230     */
231    public static Set<String> getKnownNamesAsSet() {
232        initializeKnownReplicasList();
233        Set<String> res = new HashSet<String>(knownReplicas.size());
234        for (Replica rep : knownReplicas.values()) {
235            res.add(rep.getName());
236        }
237        return res;
238    }
239
240    /**
241     * Get the type of this replica.
242     *
243     * @return The type of this replica (bitarchive or checksum).
244     */
245    public ReplicaType getType() {
246        return type;
247    }
248
249    /**
250     * Get the id of this replica.
251     *
252     * @return The id of this replica (also used in queues).
253     */
254    public String getId() {
255        return id;
256    }
257
258    /**
259     * Get the name of this replica.
260     *
261     * @return The name of this replica is known as in interface.
262     */
263    public String getName() {
264        return name;
265    }
266
267    /**
268     * Get the identification channel that corresponds to this replica. Please do not parse its name!
269     *
270     * @return The BaMon ChannelID of this replica.
271     */
272    public ChannelID getIdentificationChannel() {
273        // This is the channel used by the connected replicas in the
274        // ArcRepository.
275        if (type == ReplicaType.BITARCHIVE) {
276            return Channels.getBaMonForReplica(id);
277        } else if (type == ReplicaType.CHECKSUM) {
278            return Channels.getTheCrForReplica(id);
279        } else {
280            throw new UnknownID("No channel for replica " + toString());
281        }
282    }
283
284    /**
285     * Returns a human-readable representation of the object.
286     *
287     * @return An arbitrary string version of the object. Do not depend on its format.
288     */
289    public String toString() {
290        return type.toString() + "Replica (" + id + ") " + name;
291    }
292
293}