001/*
002 * #%L
003 * Netarchivesuite - harvester
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.harvester.datamodel;
024
025import java.sql.Connection;
026import java.sql.PreparedStatement;
027import java.sql.ResultSet;
028import java.sql.SQLException;
029import java.util.ArrayList;
030import java.util.Iterator;
031
032import dk.netarkivet.common.exceptions.ArgumentNotValid;
033import dk.netarkivet.common.exceptions.IOFailure;
034import dk.netarkivet.common.exceptions.IllegalState;
035import dk.netarkivet.common.exceptions.PermissionDenied;
036import dk.netarkivet.common.exceptions.UnknownID;
037
038/**
039 * Implementation class for the DAO handling the persistence of {@link HarvestChannel} instances.
040 *
041 * @author ngiraud
042 */
043public class HarvestChannelDBDAO extends HarvestChannelDAO {
044
045    /**
046     * Create a new HarvestChannelDAO implemented using database. This constructor also tries to upgrade the jobs and
047     * jobs_configs tables in the current database. Throws an {@link IllegalState} exception, if default channels are
048     * missing in the DB.
049     */
050    protected HarvestChannelDBDAO() {
051        Connection connection = HarvestDBConnection.get();
052        try {
053            HarvesterDatabaseTables.checkVersion(connection, HarvesterDatabaseTables.HARVESTCHANNELS);
054        } finally {
055            HarvestDBConnection.release(connection);
056        }
057
058        /*
059         * String defaultSnapshotChannelName = Settings.get(HarvesterSettings.SNAPSHOT_HARVEST_CHANNEL_ID); if
060         * (defaultSnapshotChannelName != null && defaultSnapshotChannelName.length() > 0) { HarvestChannel
061         * harvestChannel = lookupName(defaultSnapshotChannelName); if (harvestChannel == null) { harvestChannel = new
062         * HarvestChannel(defaultSnapshotChannelName, true, true, "Default channel for focused harvest.");
063         * create(harvestChannel); } }
064         */
065
066        if (!defaultFocusedChannelExists()) {
067            throw new IllegalState("No default harvest channel defined for focused jobs!");
068        }
069    }
070
071    private static final String get_by_id_sql = "SELECT * FROM harvestchannel WHERE id=?";
072
073    @Override
074    public HarvestChannel getById(final long id) throws ArgumentNotValid, UnknownID {
075        Connection connection = HarvestDBConnection.get();
076        try {
077            PreparedStatement stm = connection.prepareStatement(get_by_id_sql);
078            stm.setLong(1, id);
079            ResultSet rs = stm.executeQuery();
080            if (!rs.next()) {
081                throw new UnknownID("No harvestchannel with id " + id);
082            }
083            return buildFromResultSet(rs);
084        } catch (SQLException e) {
085            throw new UnknownID("Failed to get harvestchannel with id " + id, e);
086        } finally {
087            HarvestDBConnection.release(connection);
088        }
089    }
090
091    private static final String get_by_name_sql = "SELECT * FROM harvestchannel WHERE name=?";
092
093    @Override
094    public HarvestChannel getByName(final String name) throws ArgumentNotValid, UnknownID {
095        ArgumentNotValid.checkNotNullOrEmpty(name, "name");
096        /*
097         * if (HarvestChannel.SNAPSHOT.getName().equals(name)) { return HarvestChannel.SNAPSHOT; }
098         */
099        Connection connection = HarvestDBConnection.get();
100        try {
101            PreparedStatement stm = connection.prepareStatement(get_by_name_sql);
102            stm.setString(1, name);
103            ResultSet rs = stm.executeQuery();
104            if (!rs.next()) {
105                throw new UnknownID("No harvestchannel with name '" + name + "'");
106            }
107
108            return buildFromResultSet(rs);
109        } catch (SQLException e) {
110            throw new UnknownID("Failed to get harvestchannel with name '" + name + "'", e);
111        } finally {
112            HarvestDBConnection.release(connection);
113        }
114    }
115
116    /*
117     * private HarvestChannel lookupName(final String name) throws ArgumentNotValid, UnknownID {
118     * ArgumentNotValid.checkNotNullOrEmpty(name, "name"); HarvestChannel harvestChannel = null; Connection connection =
119     * HarvestDBConnection.get(); try { PreparedStatement stm = connection.prepareStatement( get_by_name_sql);
120     * stm.setString(1, name); ResultSet rs = stm.executeQuery(); if (rs.next()) { harvestChannel =
121     * buildFromResultSet(rs); } } catch (SQLException e) { throw new
122     * UnknownID("Failed to get harvestchannel with name '" + name + "'", e); } finally {
123     * HarvestDBConnection.release(connection); } return harvestChannel; }
124     */
125
126    private static final String create_sql = "INSERT INTO harvestchannel(name, issnapshot, isdefault, comments) VALUES (?, ?, ?, ?)";
127
128    @Override
129    public void create(final HarvestChannel harvestChan) {
130        ArgumentNotValid.checkNotNull(harvestChan, "HarvestChannel harvestChan");
131        /*
132         * if (HarvestChannel.SNAPSHOT.equals(harvestChan)) { throw new
133         * PermissionDenied("Cannot store SNAPSHOT channel!"); }
134         */
135        Connection connection = HarvestDBConnection.get();
136        try {
137            PreparedStatement stm = connection.prepareStatement(create_sql);
138            stm.setString(1, harvestChan.getName());
139            stm.setBoolean(2, harvestChan.isSnapshot());
140            stm.setBoolean(3, harvestChan.isDefault());
141            stm.setString(4, harvestChan.getComments());
142            if (stm.executeUpdate() < 1) {
143                throw new IOFailure("Failed to create harvestchannel '" + harvestChan.getName() + "'");
144            }
145        } catch (SQLException e) {
146            throw new IOFailure("Failed to create harvestchannel '" + harvestChan.getName() + "'", e);
147        } finally {
148            HarvestDBConnection.release(connection);
149        }
150    }
151
152    private static final String update_sql = "UPDATE harvestchannel SET name=?, isDefault=?, comments=? WHERE id=?";
153
154    @Override
155    public void update(HarvestChannel harvestChan) {
156        ArgumentNotValid.checkNotNull(harvestChan, "HarvestChannel harvestChan");
157        if (harvestChan.isSnapshot()) {
158            throw new PermissionDenied("Cannot update SNAPSHOT channel!");
159        }
160        Connection connection = HarvestDBConnection.get();
161        try {
162            PreparedStatement stm = connection.prepareStatement(update_sql);
163            stm.setString(1, harvestChan.getName());
164            stm.setBoolean(2, harvestChan.isDefault());
165            stm.setString(3, harvestChan.getComments());
166            stm.setLong(4, harvestChan.getId());
167            if (stm.executeUpdate() != 1) {
168                throw new IOFailure("Failed to update harvestchannel with id " + harvestChan.getId());
169            }
170        } catch (SQLException e) {
171            throw new IOFailure("Failed to update harvestchannel with id " + harvestChan.getId(), e);
172        } finally {
173            HarvestDBConnection.release(connection);
174        }
175    }
176
177    @Override
178    public Iterator<HarvestChannel> iterator() {
179        return getAll(true);
180    }
181
182    private static final String get_all_sql = "SELECT * FROM harvestchannel ORDER BY name";
183
184    @Override
185    public Iterator<HarvestChannel> getAll(final boolean includeSnapshot) {
186        Connection connection = HarvestDBConnection.get();
187        try {
188            PreparedStatement stm = connection.prepareStatement(get_all_sql);
189            ResultSet rs = stm.executeQuery();
190            ArrayList<HarvestChannel> channelList = new ArrayList<HarvestChannel>();
191            while (rs.next()) {
192                boolean isSnapshot = rs.getBoolean("issnapshot");
193                if (!isSnapshot || includeSnapshot) {
194                    channelList.add(new HarvestChannel(rs.getLong("id"), rs.getString("name"), isSnapshot, rs
195                            .getBoolean("isdefault"), rs.getString("comments")));
196                }
197            }
198            return channelList.iterator();
199        } catch (SQLException e) {
200            throw new IOFailure("Failed to get harvest channels", e);
201        } finally {
202            HarvestDBConnection.release(connection);
203        }
204    }
205
206    private static final String get_default_focused_channel_exists_sql = "SELECT * FROM harvestchannel WHERE issnapshot = true AND isdefault=true";
207
208    @Override
209    public boolean defaultFocusedChannelExists() {
210        Connection connection = HarvestDBConnection.get();
211        try {
212            PreparedStatement stm = connection.prepareStatement(get_default_focused_channel_exists_sql);
213            ResultSet rs = stm.executeQuery();
214            return rs.next();
215        } catch (SQLException e) {
216            throw new IOFailure("Failed to get default harvest channel for focused jobs", e);
217        } finally {
218            HarvestDBConnection.release(connection);
219        }
220    }
221
222    private static final String get_default_channel_sql = "SELECT * FROM harvestchannel WHERE issnapshot = ? AND isdefault=true";
223
224    @Override
225    public HarvestChannel getDefaultChannel(boolean isSnapshot) {
226        Connection connection = HarvestDBConnection.get();
227        try {
228            PreparedStatement stm = connection.prepareStatement(get_default_channel_sql);
229            stm.setBoolean(1, isSnapshot);
230            ResultSet rs = stm.executeQuery();
231            if (!rs.next()) {
232                throw new IOFailure("No default harvest channel for snapshot=" + isSnapshot);
233            }
234            return buildFromResultSet(rs);
235        } catch (SQLException e) {
236            throw new IOFailure("Failed to get default harvest channel for snapshot=" + isSnapshot, e);
237        } finally {
238            HarvestDBConnection.release(connection);
239        }
240    }
241
242    private static final String get_channel_for_hd_sql;
243
244    static {
245        get_channel_for_hd_sql = "SELECT * FROM harvestchannel C, harvestdefinitions D "
246                + "WHERE D.channel_id=C.id AND D.harvest_id=?";
247    }
248
249    @Override
250    public HarvestChannel getChannelForHarvestDefinition(long harvestDefinitionId) {
251        Connection connection = HarvestDBConnection.get();
252        try {
253            PreparedStatement stm = connection.prepareStatement(get_channel_for_hd_sql);
254            stm.setLong(1, harvestDefinitionId);
255            ResultSet rs = stm.executeQuery();
256            if (!rs.next()) {
257                return null;
258            }
259            return buildFromResultSet(rs);
260        } catch (SQLException e) {
261            throw new IOFailure("Failed to find harvestdefinition-channel association", e);
262        } finally {
263            HarvestDBConnection.release(connection);
264        }
265    }
266
267    private HarvestChannel buildFromResultSet(ResultSet rs) throws SQLException {
268        return new HarvestChannel(rs.getLong("id"), rs.getString("name"), rs.getBoolean("issnapshot"),
269                rs.getBoolean("isdefault"), rs.getString("comments"));
270    }
271
272}