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 */
023
024package dk.netarkivet.harvester.datamodel;
025
026import java.io.Reader;
027import java.io.StringReader;
028import java.sql.Clob;
029import java.sql.Connection;
030import java.sql.PreparedStatement;
031import java.sql.ResultSet;
032import java.sql.SQLException;
033import java.util.Iterator;
034import java.util.List;
035
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039import dk.netarkivet.common.exceptions.ArgumentNotValid;
040import dk.netarkivet.common.exceptions.IOFailure;
041import dk.netarkivet.common.exceptions.PermissionDenied;
042import dk.netarkivet.common.exceptions.UnknownID;
043import dk.netarkivet.common.utils.DBUtils;
044import dk.netarkivet.common.utils.ExceptionUtils;
045
046/**
047 * Implements the TemplateDAO with databases.
048 * <p>
049 * The statements to create the tables are now in scripts/sql/createfullhddb.sql
050 */
051
052public class TemplateDBDAO extends TemplateDAO {
053
054    /** the log. */
055    private static final Logger log = LoggerFactory.getLogger(TemplateDBDAO.class);
056
057    /**
058     * Default constructor. Only used by TemplateDAO,getInstance().
059     */
060    TemplateDBDAO() {
061        Connection connection = HarvestDBConnection.get();
062        try {
063            HarvesterDatabaseTables.checkVersion(connection, HarvesterDatabaseTables.ORDERTEMPLATES);
064        } finally {
065            HarvestDBConnection.release(connection);
066        }
067    }
068
069    /**
070     * Read an XML order file for the named order XML.
071     *
072     * @param orderXmlName The name of the order.xml document
073     * @return The contents of this order.xml document
074     */
075    public synchronized HeritrixTemplate read(String orderXmlName) {
076        ArgumentNotValid.checkNotNullOrEmpty(orderXmlName, "String orderXmlName");
077        Connection c = HarvestDBConnection.get();
078        PreparedStatement s = null;
079        log.debug("Reading template {} from database", orderXmlName);
080        try {
081            s = c.prepareStatement("SELECT template_id, orderxml, isActive FROM ordertemplates WHERE name = ?");
082            s.setString(1, orderXmlName);
083            ResultSet res = s.executeQuery();
084            if (!res.next()) {
085                throw new UnknownID("Can't find template " + orderXmlName);
086            }
087            Reader orderTemplateReader = null;
088                long template_id = res.getLong(1);
089            if (DBSpecifics.getInstance().supportsClob()) {
090                Clob clob = res.getClob(2);
091                orderTemplateReader = clob.getCharacterStream();
092            } else {
093                String string = res.getString(2);
094                orderTemplateReader = new StringReader(string);
095            } 
096            HeritrixTemplate heritrixTemplate = HeritrixTemplate.read(template_id, orderTemplateReader);
097            heritrixTemplate.setIsActive(res.getBoolean(3));
098            return heritrixTemplate;
099        } catch (SQLException e) {
100            final String message = "SQL error finding order.xml for " + orderXmlName + "\n"
101                    + ExceptionUtils.getSQLExceptionCause(e);
102            log.warn(message, e);
103            throw new IOFailure(message, e);
104        }
105         finally {
106            DBUtils.closeStatementIfOpen(s);
107            HarvestDBConnection.release(c);
108        }
109    }
110
111    /**
112     * Returns an iterator with all names of order.xml-templates.
113     *
114     * @return Iterator<String> with all names of templates (without .xml).
115     */
116    public synchronized Iterator<String> getAll() {
117        Connection c = HarvestDBConnection.get();
118        try {
119            List<String> names = DBUtils.selectStringList(c, "SELECT name FROM ordertemplates ORDER BY name");
120            return names.iterator();
121        } finally {
122            HarvestDBConnection.release(c);
123        }
124    }
125
126    @Override
127    public synchronized Iterator<String> getAll(boolean active) {
128        Connection c = HarvestDBConnection.get();
129        try {
130            List<String> names = DBUtils.selectStringList(c, "SELECT name FROM ordertemplates WHERE isActive=? ORDER BY name ", active);
131            return names.iterator();
132        } finally {
133            HarvestDBConnection.release(c);
134        }
135    }
136
137    /**
138     * Return true if the database contains a template with the given name.
139     *
140     * @param orderXmlName Name of an order.xml template (without .xml).
141     * @return True if such a template exists.
142     * @throws ArgumentNotValid If the orderXmlName is null or an empty String
143     */
144    public synchronized boolean exists(String orderXmlName) {
145        ArgumentNotValid.checkNotNullOrEmpty(orderXmlName, "String orderXmlName");
146
147        Connection c = HarvestDBConnection.get();
148        try {
149            return exists(c, orderXmlName);
150        } finally {
151            HarvestDBConnection.release(c);
152        }
153    }
154
155    /**
156     * Return true if the database contains a template with the given name.
157     *
158     * @param orderXmlName Name of an order.xml template (without .xml).
159     * @return True if such a template exists.
160     * @throws ArgumentNotValid If the orderXmlName is null or an empty String
161     */
162    private synchronized boolean exists(Connection c, String orderXmlName) {
163        int count = DBUtils.selectIntValue(c, "SELECT COUNT(*) FROM ordertemplates WHERE name = ?", orderXmlName);
164        return count == 1;
165    }
166
167    /**
168     * Create a template. The template must not already exist.
169     *
170     * @param orderXmlName Name of the template.
171     * @param orderXml XML documents that is a Heritrix order.xml template.
172     * @throws ArgumentNotValid If the orderXmlName is null or an empty String, or the orderXml is null.
173     */
174    public synchronized void create(String orderXmlName, HeritrixTemplate orderXml) {
175        ArgumentNotValid.checkNotNullOrEmpty(orderXmlName, "String orderXmlName");
176        ArgumentNotValid.checkNotNull(orderXml, "HeritrixTemplate orderXml");
177
178        Connection c = HarvestDBConnection.get();
179        PreparedStatement s = null;
180        try {
181            if (exists(c, orderXmlName)) {
182                throw new PermissionDenied("An order template called " + orderXmlName + " already exists");
183            }
184
185            s = c.prepareStatement("INSERT INTO ordertemplates " + "( name, orderxml, isActive ) VALUES ( ?, ?, ? )");
186            DBUtils.setStringMaxLength(s, 1, orderXmlName, Constants.MAX_NAME_SIZE, orderXmlName, "length");
187            DBUtils.setClobMaxLength(s, 2, orderXml.getXML(), Constants.MAX_ORDERXML_SIZE, "size", orderXmlName);
188            s.setBoolean(3, orderXml.isActive());
189            s.executeUpdate();
190        } catch (SQLException e) {
191            throw new IOFailure("SQL error creating template " + orderXmlName + "\n"
192                    + ExceptionUtils.getSQLExceptionCause(e), e);
193        } finally {
194            HarvestDBConnection.release(c);
195        }
196    }
197
198    /**
199     * Update a template. The template must already exist.
200     *
201     * @param orderXmlName Name of the template.
202     * @param orderXml XML document that is a Heritrix order.xml template.
203     * @throws PermissionDenied If the template does not exist
204     * @throws IOFailure If the template could not be
205     * @throws ArgumentNotValid If the orderXmlName is null or an empty String, or the orderXml is null.
206     */
207    public synchronized void update(String orderXmlName, HeritrixTemplate orderXml) {
208        ArgumentNotValid.checkNotNullOrEmpty(orderXmlName, "String orderXmlName");
209        ArgumentNotValid.checkNotNull(orderXml, "HeritrixTemplate orderXml");
210
211        Connection c = HarvestDBConnection.get();
212        PreparedStatement s = null;
213        try {
214            if (!exists(c, orderXmlName)) {
215                throw new PermissionDenied("No order template called " + orderXmlName + " exists");
216            }
217
218            s = c.prepareStatement("UPDATE ordertemplates SET orderxml = ?, isActive= ? WHERE name = ?");
219            DBUtils.setClobMaxLength(s, 1, orderXml.getXML(), Constants.MAX_ORDERXML_SIZE, "size", orderXmlName);
220            s.setBoolean(2, orderXml.isActive());
221            s.setString(3, orderXmlName);
222            s.executeUpdate();
223        } catch (SQLException e) {
224            throw new IOFailure("SQL error updating template " + orderXmlName + "\n"
225                    + ExceptionUtils.getSQLExceptionCause(e), e);
226        } finally {
227            HarvestDBConnection.release(c);
228        }
229    }
230
231}