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 */
023package dk.netarkivet.common.distribute;
024
025import java.io.ObjectInputStream;
026import java.io.ObjectOutputStream;
027import java.io.Serializable;
028
029import dk.netarkivet.common.CommonSettings;
030import dk.netarkivet.common.exceptions.ArgumentNotValid;
031import dk.netarkivet.common.exceptions.IOFailure;
032import dk.netarkivet.common.exceptions.UnknownID;
033import dk.netarkivet.common.utils.Settings;
034import dk.netarkivet.common.utils.SystemUtils;
035
036/**
037 * A class for representing the names of JMS queues.
038 */
039@SuppressWarnings({"serial"})
040public class ChannelID implements Serializable {
041
042    /**
043     * The name of the enviroment in which this process is running. It is used for prefixing all ChannelIDs. An example
044     * value is "PROD".
045     */
046    private final String environmentName = Settings.get(CommonSettings.ENVIRONMENT_NAME);
047
048    /**
049     * application instance id is a setting for an application's identification as a specific process on a given
050     * machine. An example value is "BAONE" Note that it is set to uppercase in order to ensure that a channel name only
051     * has uppercase characters.
052     */
053    private static final String applicationInstanceId = Settings.get(CommonSettings.APPLICATION_INSTANCE_ID)
054            .toUpperCase();
055
056    /**
057     * application instance id is a setting for an application's identification as a specific process on a given
058     * machine. An example value is "BAONE".
059     */
060    private static final String applicationAbbreviation = getApplicationAbbreviation(Settings
061            .get(CommonSettings.APPLICATION_NAME));
062
063    /**
064     * Constants to make the semantics of parameters to our name constructors more explicit.
065     */
066    public static final String COMMON = "COMMON";
067    public static final boolean INCLUDE_IP = true;
068    public static final boolean NO_IP = false;
069    public static final boolean INCLUDE_APPLINST_ID = true;
070    public static final boolean NO_APPLINST_ID = false;
071    public static final boolean TOPIC = true;
072    public static final boolean QUEUE = false;
073
074    /**
075     * A ChannelID is identified by its name. It has one bit of state information: is it a queue or a topic?
076     */
077    private String name;
078
079    /**
080     * Constructor of channel names. The constructor is package private because we should never use any channels except
081     * the ones constructed by our friend Channels.java
082     *
083     * @param appPref The prefix used for the applications listening to the channel.
084     * @param replicaId Name of the replica, or ChannelID.COMMON if channel shared by all replicas.
085     * @param useNodeId Whether that IP address of the local node should be included in the channel name.
086     * @param useAppInstId Whether application instance id from settings should be included in the channel name.
087     * @param isTopic Whether the Channel is a Topic or a Queue.
088     * @throws UnknownID if looking up the local IP number failed.
089     */
090    public ChannelID(String appPref, String replicaId, boolean useNodeId, boolean useAppInstId, boolean isTopic) {
091        this.name = constructName(appPref, replicaId, useNodeId, useAppInstId, isTopic);
092    }
093
094    /**
095     * Constructs a channel name according to the specifications of channels in the NetarchiveSuite Developer Manual.
096     *
097     * @param appPref The prefix used for the applications listening to the channel.
098     * @param replicaId Id of the replica, or ChannelID.COMMON if channel common to all bitarchive replicas.
099     * @param useNodeId Whether that IP address of the local node should be included in the channel name.
100     * @param useAppInstId Whether application instance id from settings should be included in the channel name.
101     * @param isTopic If true, the channel is a Topic, else it is a Queue
102     * @return The properly concatenated channel name.
103     * @throws UnknownID if looking up the local IP number failed.
104     */
105    private String constructName(String appPref, String replicaId, boolean useNodeId, boolean useAppInstId,
106            boolean isTopic) {
107        String userId = environmentName;
108        String id = "";
109        if (useNodeId) {
110            // Replace the '.' in the IP-address with '_'
111            id = SystemUtils.getLocalIP().replace('.', '_');
112            if (useAppInstId) {
113                id += Channels.CHANNEL_PART_SEPARATOR + applicationAbbreviation;
114                if (!applicationInstanceId.isEmpty()) {
115                    id += (Channels.CHANNEL_PART_SEPARATOR + applicationInstanceId);
116                }
117            }
118        }
119
120        String resultingName = userId + Channels.CHANNEL_PART_SEPARATOR + replicaId + Channels.CHANNEL_PART_SEPARATOR
121                + appPref;
122        if (!id.isEmpty()) {
123            resultingName += Channels.CHANNEL_PART_SEPARATOR + id;
124        }
125        if (isTopic) {
126            resultingName += Channels.CHANNEL_PART_SEPARATOR + "TOPIC";
127        }
128        return resultingName;
129    }
130
131    /**
132     * Getter for the channel name.
133     *
134     * @return The name of the channel referred to by this object.
135     */
136    public String getName() {
137        return name;
138    }
139
140    /**
141     * Pretty-printer.
142     *
143     * @return a nice String representation of the ChannelID.
144     */
145    public String toString() {
146        return Channels.isTopic(name) ? ("[Topic '" + name + "']") : ("[Queue '" + name + "']");
147    }
148
149    /**
150     * Method used by Java deserialization. Our coding guidelines prescribes that this method should always be
151     * implemented, even if it only calls the default method:
152     * http://kb-prod-udv-001.kb.dk/twiki/bin/view/Netarkiv/ImplementeringOgTestAfSerializable See also
153     * "Effective Java", pages 219 and 224.
154     *
155     * @param ois the ObjectInputStream used to read in the object
156     * @throws IOFailure if Java could not deserialize the object.
157     */
158    private void readObject(ObjectInputStream ois) {
159        try {
160            ois.defaultReadObject();
161        } catch (Exception e) {
162            throw new IOFailure("Standard deserialization of ChannelID failed.", e);
163        }
164    }
165
166    /**
167     * Method used by Java serialization. Our coding guidelines prescribes that this method should always be
168     * implemented, even if it only calls the default method:
169     * http://kb-prod-udv-001.kb.dk/twiki/bin/view/Netarkiv/ImplementeringOgTestAfSerializable See also
170     * "Effective Java", pages 219 and 224.
171     *
172     * @param oos the ObjectOutputStream used to serialize the object.
173     * @throws IOFailure if Java could not serialize the object.
174     */
175    private void writeObject(ObjectOutputStream oos) {
176        try {
177            oos.defaultWriteObject();
178        } catch (Exception e) {
179            throw new IOFailure("Standard serialization of ChannelID failed.", e);
180        }
181    }
182
183    /**
184     * Implements equality check for ChannelIDs. Useful when these are used as indexes in Java collections, for
185     * instance.
186     *
187     * @param o The object to compare this object with.
188     * @return Whether o and this should be considered the same ChannelID.
189     */
190    public boolean equals(Object o) {
191        if (this == o) {
192            return true;
193        }
194        if (!(o instanceof ChannelID)) {
195            return false;
196        }
197        final ChannelID channelID = (ChannelID) o;
198        if (!name.equals(channelID.name)) {
199            return false;
200        }
201        return true;
202    }
203
204    /**
205     * Computes a hash code based on the channel name and whether it is a topic.
206     *
207     * @return A hash code for this object.
208     */
209    public int hashCode() {
210        int result;
211        result = name.hashCode();
212        return result;
213    }
214
215    /**
216     * Finds abbreviation for an application name. The abbreviation is only calculated from the application name without
217     * path. It is made from the uppercase letters in the name.
218     *
219     * @param applName application name with full path
220     * @return abbreviation for given application name.
221     */
222    private static String getApplicationAbbreviation(String applName) {
223        ArgumentNotValid.checkNotNull(applName, "applName");
224        // Strip path from name
225        String[] p = applName.split("[.]");
226        if (p.length <= 0) {
227            return "";
228        }
229        String shortName = p[p.length - 1];
230        // put uppercase letters into abbr
231        String abbr = "";
232        for (int i = 0; i < shortName.length(); i++) {
233            if (Character.isUpperCase(shortName.charAt(i))) {
234                abbr += shortName.substring(i, i + 1);
235            }
236        }
237        // return found abbreviation
238        return abbr;
239    }
240
241}