001/*
002 * #%L
003 * Netarchivesuite - harvester
004 * %%
005 * Copyright (C) 2005 - 2018 The Royal Danish 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.harvester.datamodel;
024
025import java.io.Serializable;
026
027import javax.servlet.jsp.PageContext;
028
029import dk.netarkivet.common.exceptions.ArgumentNotValid;
030import dk.netarkivet.common.utils.I18n;
031
032/**
033 * Harvest channels are used to dispatch harvest jobs to specific pools of crawlers. Channels can accept either only
034 * snapshot jobs or only focused jobs. Snapshot crawls all use a single hard-coded channel.
035 * <p>
036 * Harvest channels names must only contain alphanumeric characters, the constraint is enforced at creation time.
037 * <p>
038 * {@link HarvestDefinition}s are mapped to a {@link HarvestChannel}, and HarvestControllers listen to jobs sent
039 * on a specific channel.
040 * <p>
041 * Harvest channels are stored in the harvest database, as well as mappings to {@link HarvestDefinition}s and
042 * HarvestControllers through two association tables.
043 * <p>
044 * There must be exactly one channel defined as default for every type of job (snapshot and focused). This constraint
045 * will be enforced by the DAO.
046 *
047 * @author ngiraud
048 */
049@SuppressWarnings("serial")
050public class HarvestChannel implements Serializable {
051
052    /** Defines acceptable channel names: at least one word character. */
053    public static final String ACCEPTABLE_NAME_PATTERN = "^\\w+$";
054
055    /** The unique numeric id. */
056    private long id;
057
058    /**
059     * The unique name of the channel. Accepts only alpha numeric characters.
060     *
061     * @see #ACCEPTABLE_NAME_PATTERN
062     * @see #isAcceptableName(String)
063     */
064    private String name;
065
066    /** Whether this channels type is snapshot or focused. */
067    private boolean isSnapshot;
068
069    /** Whether this channel is the default one for the given type (snapshot or focused). */
070    private boolean isDefault;
071
072    /** Comments. */
073    private String comments;
074
075    /**
076     * Constructor from name and comments.
077     *
078     * @param name channel name
079     * @param isSnapshot whether this channels type is snapshot or focused
080     * @param isDefault whether this channel is the default one
081     * @param comments user comments (snapshot or focused)
082     * @throws ArgumentNotValid if the name is incorrect.
083     */
084    public HarvestChannel(String name, boolean isSnapshot, boolean isDefault, String comments) {
085        if (!isAcceptableName(name)) {
086            throw new ArgumentNotValid("'" + name + "' does not match pattern '" + ACCEPTABLE_NAME_PATTERN + "'");
087        }
088        this.name = name;
089        this.isSnapshot = isSnapshot;
090        this.isDefault = isDefault;
091        this.comments = comments;
092    }
093
094    /**
095     * Constructor from persistent storage.
096     *
097     * @param id the channel id
098     * @param name channel name
099     * @param isSnapshot whether this channels type is snapshot or focused
100     * @param isDefault whether this channel is the default one for the given type
101     * @param comments user comments
102     * @throws ArgumentNotValid if the name is incorrect.
103     */
104    public HarvestChannel(long id, String name, boolean isSnapshot, boolean isDefault, String comments) {
105        ArgumentNotValid.checkNotNullOrEmpty(name, "name");
106        if (!isAcceptableName(name)) {
107            throw new ArgumentNotValid("'" + name + "' does not match pattern '" + ACCEPTABLE_NAME_PATTERN + "'");
108        }
109        this.id = id;
110        this.name = name;
111        this.isSnapshot = isSnapshot;
112        this.isDefault = isDefault;
113        this.comments = comments;
114    }
115
116    /**
117     * @return the unique identifier in the persistent storage
118     */
119    public long getId() {
120        return id;
121    }
122
123    /**
124     * @return the harvest channel name.
125     */
126    public String getName() {
127        return name;
128    }
129
130    /**
131     * Sets the harvest channel name
132     *
133     * @param name the name to set
134     */
135    public void setName(String name) {
136        this.name = name;
137    }
138
139    /**
140     * @return true if this channel is intended for snaphsot harvests, false if it is intended for focused ones.
141     */
142    public boolean isSnapshot() {
143        return isSnapshot;
144    }
145
146    /**
147     * Set the harvest type to snapshot or focused.
148     *
149     * @param isSnapshot true if snapshot, false if focused
150     */
151    public void setSnapshot(boolean isSnapshot) {
152        this.isSnapshot = isSnapshot;
153    }
154
155    /**
156     * @return true if the channel is the default one for the harvest type (snapshot or focused), false otherwise.
157     */
158    public boolean isDefault() {
159        return isDefault;
160    }
161
162    /**
163     * Set whether if the channel is the default one for the harvest type (snapshot or focused).
164     *
165     * @param isDefault true if default, false otherwise
166     */
167    public void setDefault(boolean isDefault) {
168        this.isDefault = isDefault;
169    }
170
171    /**
172     * @return the associated comments.
173     */
174    public String getComments() {
175        return comments;
176    }
177
178    /**
179     * Sets the associated comments
180     *
181     * @param comments the comments to set
182     */
183    public void setComments(String comments) {
184        this.comments = comments;
185    }
186
187    /**
188     * Renders a localized description for the singleton.
189     *
190     * @param context
191     * @return a localized description.
192     */
193    public static String getSnapshotDescription(PageContext context) {
194        return I18n.getString(dk.netarkivet.harvester.Constants.TRANSLATIONS_BUNDLE, context.getResponse().getLocale(),
195                "harvest.channel.snapshot.desc");
196    }
197
198    /**
199     * Returns true if the given input is an acceptable channel name.
200     *
201     * @param input the candidate name.
202     * @return true if the name complies to the defined {@link #ACCEPTABLE_NAME_PATTERN}, false otherwise
203     */
204    public static boolean isAcceptableName(String input) {
205        return input.matches(ACCEPTABLE_NAME_PATTERN);
206    }
207
208    @Override
209    public String toString() {
210        return "HarvestChannel [id=" + id + ", name=" + name + ", comments=" + comments + ", isSnapShot=" + isSnapshot
211                + ", isDefault=" + isDefault + "]";
212    }
213
214}