com.daemon.database.SearchTerm.java Source code

Java tutorial

Introduction

Here is the source code for com.daemon.database.SearchTerm.java

Source

/*******************************************************************************
 * This file is part of Tmetrics.
 *
 * Tmetrics is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tmetrics is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tmetrics. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package com.daemon.database;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Properties;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;
import com.tmetrics.util.HashCodeUtil;
import com.tmetrics.util.Localization;

/**
 * Represents a search term to be saved to and loaded from the database for
 * the ease of use.
 * The methods for loading and inserting a search term are marked static.
 * @author Torsten, Jens
 */
public class SearchTerm {

    public static String DATABASE_PROPERTY_PATH = System.getProperty("user.home") + "/database.properties";

    // Used for converting SQL Time to joda-time Duration
    public static final PeriodFormatter DURATION_FORMATTER = new PeriodFormatterBuilder().appendHours()
            .appendSeparator(":").appendMinutes().appendSeparator(":").appendSeconds().toFormatter();

    private Integer _id = null;
    private String _term = null;
    private boolean _isActive = true;
    private DateTime _current_start = null;
    private DateTime _old_start = null;
    private Duration _interval_length = null;
    private Long _lastFetchedTweetId = null;
    private Integer _priority = null;
    private DateTime _timeLastFetched = null;
    private Integer _lastFetchedTweetCount = null;
    private DateTime _whenCreated = null;

    /**
     * Creates a new search term object with default values.
     * @param name The name of the search term object.
     * @param currentStart The date of the current start / when the search term was created.
     */
    public SearchTerm(String name, DateTime currentStart) {
        _term = name;
        _current_start = currentStart;

        // Default value for interval length is 15 min.
        _interval_length = DURATION_FORMATTER.parsePeriod("00:15:00").toStandardDuration();
        // Default value for _priority is 0(normal priority)
        _priority = 0;

        _whenCreated = currentStart;
    }

    /**
     * Creates a search term object by converting it from a result set
     * 
     * @param resultSet ResultSet to create search term object from
     * @throws SQLException Thrown if there is a MySQL problem
     */
    public SearchTerm(ResultSet resultSet) throws SQLException {
        _id = resultSet.getInt(1);
        _term = resultSet.getString(2);
        _isActive = resultSet.getBoolean(3);
        _current_start = new DateTime(resultSet.getTimestamp(4, Localization.UTC).getTime());
        Timestamp oldStart = resultSet.getTimestamp(5, Localization.UTC);
        if (resultSet.wasNull()) {
            _old_start = null;
        } else {
            _old_start = new DateTime(oldStart.getTime());
        }

        // Dirty hack: Because we cannot get every time via getTimestamp, we have to get the byte stream
        // and convert it into a string time.
        byte[] arr = resultSet.getBytes(6);
        String intervalLength = new String(arr);
        _interval_length = Localization.DURATION_FORMATTER.parsePeriod(intervalLength).toStandardDuration();

        _priority = resultSet.getInt(7);
        if (resultSet.wasNull())
            _priority = null;
        Timestamp timeLastFetched = resultSet.getTimestamp(8, Localization.UTC);
        if (resultSet.wasNull()) {
            _timeLastFetched = null;
        } else {
            _timeLastFetched = new DateTime(timeLastFetched.getTime());
        }
        _lastFetchedTweetId = resultSet.getLong(9);
        if (resultSet.wasNull())
            _lastFetchedTweetId = null;
        _lastFetchedTweetCount = resultSet.getInt(10);
        if (resultSet.wasNull())
            _lastFetchedTweetCount = null;
        _whenCreated = new DateTime(resultSet.getTimestamp(11, Localization.UTC).getTime());
    }

    /**
     * Copy constructor.
     */
    public SearchTerm(SearchTerm term) {
        _id = term._id;
        _term = term._term;
        _isActive = term._isActive;
        _current_start = new DateTime(term._current_start.getMillis());
        if (term._old_start != null)
            _old_start = new DateTime(term._old_start.getMillis());
        _interval_length = new Duration(term._interval_length.getMillis());
        _lastFetchedTweetId = term._lastFetchedTweetId;
        _priority = term._priority;
        if (term._timeLastFetched != null)
            _timeLastFetched = new DateTime(term._timeLastFetched.getMillis());
        _lastFetchedTweetCount = term._lastFetchedTweetCount;
        _whenCreated = new DateTime(term._whenCreated.getMillis());
    }

    /**
     * Returns the Id of the search term object.
     * @return Returns the Id of the search term object.
     * @throws Exception Thrown when this object does not have an Id, yet.
     */
    public int getId() throws Exception {
        if (_id == null) {
            throw new Exception("This search term object does not have an Id, yet.");
        }

        return _id;
    }

    /**
     * Return the name of the search term object.
     * @return Return the name of the search term object.
     */
    public String getTerm() {
        return _term;
    }

    /**
     * Sets the name of the search term object.
     * @return Sets the name of the search term object.
     */
    public void setTerm(String term) {
        _term = term;
    }

    /**
     * Returns whether the search term is active or not.
     * @return Returns whether the search term is active or not.
     */
    public boolean isActive() {
        return _isActive;
    }

    /**
     * Sets whether the search term shall be marked as active or not.
     * @param isActive The activity of the search term.
     */
    public void setActive(boolean isActive) {
        _isActive = isActive;
    }

    /**
     * Returns the Id of the oldest fetched tweet.
     * @return Returns the Id of the oldest fetched tweet for future searches up to this
     * Id.
     */
    public Long getLastFetchedTweetId() {
        return _lastFetchedTweetId;
    }

    /**
     * Sets the Id of the last fetched oldest tweet for this search term.
     * @param lastFetchedTweetId The Id of the last fetched oldest tweet.
     */
    public void setLastFetchedTweetId(Long lastFetchedTweetId) {
        _lastFetchedTweetId = lastFetchedTweetId;
    }

    /**
     * Gets the user priority. Influences the factor the interval length
     * will get multiplied by. Corresponding Values are in the Daemon properties
     * @return Returns the user-set priority. Valid Values: -2, -1, 0, 1, 2. Default: 0
     */
    public Integer getPriority() {
        return _priority;
    }

    /**
     * Sets the user priority. Influences the factor the interval length
     * will get multiplied by. Corresponding Values are in the Daemon properties
     * 
     * @param priority The user set priority. Valid Values are -2, -1, 0, 1, 2,
     * and 0 is default i.e. no special priority. 
     */
    public void setPriority(Integer priority) {
        _priority = priority;
    }

    /**
     * Returns the time we last searched for this term.
     * @return Returns the time we last searched for this term as DateTime.
     */
    public DateTime getTimeLastFetched() {
        return _timeLastFetched;
    }

    /**
     * Sets the time we last searched for this term.
     * @param timeLastFetched The time of our last search for this term as DateTime
     */
    public void setTimeLastFetched(DateTime timeLastFetched) {
        _timeLastFetched = timeLastFetched;
    }

    /**
     * Returns the number of tweets received within last lookup for this search term.
     * If the count is >= 100, it means that the Daemon got to many tweets to handle and will
     * reduce its stepwidth accordingly. Alternatively if the count is far below 100, it
     * means the daemon can increase its stepwidth to find more tweets within one request.
     * @return The number of tweets received within last lookup.
     */
    public Integer getLastFetchedTweetCount() {
        return _lastFetchedTweetCount;
    }

    /**
     * Sets the number of tweets received within last lookup for this search term.
     * If the count is >= 100, it means that the Daemon got to many tweets to handle and will
     * reduce its stepwidth accordingly. Alternatively if the count is far below 100, it
     * means the daemon can increase its stepwidth to find more tweets within one request.
     * @param lastFetchedTweetCount The number of tweets received within last lookup.
     */
    public void setLastFetchedTweetCount(Integer lastFetchedTweetCount) {
        _lastFetchedTweetCount = lastFetchedTweetCount;
    }

    /**
     * Gets the value of current_start. Not Null.
     * First set by Rest Service, then updated by the daemon.
     * @return _current_start Date of the current start
     */
    public DateTime getCurrentStart() {
        return _current_start;
    }

    /**
     * Sets the value of the current_start as DateTime
     * @param _current_start
     */
    public void setCurrentStart(DateTime current_start) {
        if (_current_start != null) {
            this._current_start = current_start;
        } else {
            throw new InvalidParameterException("current_start must not be NULL");
        }
    }

    /**
     * Gets the DateTime of the old_start value in the DB
     * @return _old_start Datetime of the Value of old_start
     */
    public DateTime getOldStart() {
        return _old_start;
    }

    /**
     * Sets the Date and time (value) of the old_start as DateTime
     */
    public void setOldStart(DateTime old_start) {
        this._old_start = old_start;
    }

    /**
     * Gets the value of interval_length as joda Time Duration.
     * @return interval_length
     */
    public Duration getIntervalLength() {
        return _interval_length;
    }

    /**
     * Sets the interval_length as Joda Time Duration
     * @param interval_length
     */
    public void setIntervalLength(Duration interval_length) {
        _interval_length = interval_length;
    }

    /**
     * Returns the value of when_created.
     * Should only set by the Rest Service
     * @return when_started DateTime
     */
    public DateTime getWhenCreated() {
        return _whenCreated;
    }

    /**
     * Sets the when_created Date for a Search Term, Expects a DateTime.
     */
    public void setWhenCreated(DateTime whenCreated) {
        this._whenCreated = whenCreated;
    }

    /**
     * Load a search term object identified by its Id from the database
     * and in a java search term object. Should only be used by the Tests.
     * Throws a SQLException if the id is not found in the Database
     * 
     * @param id The id identifying the search term object.
     * @return Returns the correct search term object or null, if there is
     * no such item identified by Id.
     */
    public static SearchTerm load(int id) throws SQLException {
        String sqlSelect = "SELECT * FROM search_terms WHERE id = ?";

        Connection conn = null;
        PreparedStatement prepStatement = null;
        ResultSet resSet = null;

        SearchTerm searchTerm = null;
        FileInputStream fis = null;

        try {
            Properties props = new Properties();
            fis = new FileInputStream(DATABASE_PROPERTY_PATH);
            props.load(fis);
            Class.forName(props.getProperty("javabase.jdbc.driver"));

            String dbUrl = props.getProperty("javabase.jdbc.url") + props.getProperty("database.name") + "?user="
                    + props.getProperty("javabase.jdbc.username") + "&password="
                    + props.getProperty("javabase.jdbc.password") + "&useLegacyDatetimeCode=false"
                    + "&serverTimezone=UTC";
            conn = DriverManager.getConnection(dbUrl);

            // Get the row
            prepStatement = conn.prepareStatement(sqlSelect);
            prepStatement.setInt(1, id);
            prepStatement.execute();
            resSet = prepStatement.getResultSet();
            resSet.first();

            // Fill the object

            // column - name
            // 2 - term, 4 - current_start
            searchTerm = new SearchTerm(resSet.getString(2),
                    new DateTime(resSet.getTimestamp(4, Localization.UTC).getTime()));
            //1 id
            searchTerm._id = resSet.getInt(1);
            // 3 active
            searchTerm.setActive(resSet.getBoolean(3));
            // 5 old_start
            Timestamp oldStart = resSet.getTimestamp(5, Localization.UTC);
            if (!resSet.wasNull())
                searchTerm.setOldStart(new DateTime(oldStart.getTime()));
            // 6 interval_length

            // Dirty hack: Because we cannot get every time via getTimestamp, we have to get
            // the byte stream and convert it into a string time.
            byte[] arr = resSet.getBytes(6);
            String intervalLength = new String(arr);
            searchTerm.setIntervalLength(
                    new Duration(Localization.DURATION_FORMATTER.parsePeriod(intervalLength).toStandardDuration()));

            // 7 priority
            searchTerm.setPriority(resSet.getInt(7));
            if (resSet.wasNull())
                searchTerm.setPriority(null);
            // 8 time_last_fetched
            Timestamp lastFetched = resSet.getTimestamp(8, Localization.UTC);
            if (!resSet.wasNull())
                searchTerm.setTimeLastFetched(new DateTime(lastFetched.getTime()));
            // 9 last_fetched_tweet_id
            Long lastFetchedTweetId = resSet.getLong(9);
            if (!resSet.wasNull())
                searchTerm.setLastFetchedTweetId(lastFetchedTweetId);
            // 10 last_fetched_tweet_count
            searchTerm.setLastFetchedTweetCount(resSet.getInt(10));
            if (resSet.wasNull())
                searchTerm.setLastFetchedTweetCount(null);
            //11 when_created
            Timestamp when_created = resSet.getTimestamp(11, Localization.UTC);
            searchTerm.setWhenCreated(new DateTime(when_created.getTime()));

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException sqlEx) {
            sqlEx.printStackTrace();
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (resSet != null)
                resSet.close();

            if (prepStatement != null)
                prepStatement.close();

            if (conn != null)
                conn.close();
        }

        return searchTerm;
    }

    /**
     * Load a search term object identified by its Id from the database
     * and in a java search term object. Should only be used by the Tests.
     * Throws a SQLException if the id is not found in the Database
     * 
     * @param term The name identifying the search term object.
     * @return Returns the correct search term object or null, if there is
     * no such item identified by term.
     */
    public static SearchTerm load(String term) throws SQLException {
        String sqlSelect = "SELECT * FROM search_terms WHERE term = ?";

        Connection conn = null;
        PreparedStatement prepStatement = null;
        ResultSet resSet = null;

        SearchTerm searchTerm = null;
        FileInputStream fis = null;

        try {
            Properties props = new Properties();
            fis = new FileInputStream(DATABASE_PROPERTY_PATH);
            props.load(fis);
            Class.forName(props.getProperty("javabase.jdbc.driver"));

            String dbUrl = props.getProperty("javabase.jdbc.url") + props.getProperty("database.name") + "?user="
                    + props.getProperty("javabase.jdbc.username") + "&password="
                    + props.getProperty("javabase.jdbc.password") + "&useLegacyDatetimeCode=false"
                    + "&serverTimezone=UTC";
            conn = DriverManager.getConnection(dbUrl);

            // Get the row
            prepStatement = conn.prepareStatement(sqlSelect);
            prepStatement.setString(1, term);
            prepStatement.execute();
            resSet = prepStatement.getResultSet();
            resSet.first();

            // Fill the object

            // column - name
            // 2 - term, 4 - current_start
            searchTerm = new SearchTerm(resSet.getString(2),
                    new DateTime(resSet.getTimestamp(4, Localization.UTC).getTime()));
            //1 id
            searchTerm._id = resSet.getInt(1);
            // 3 active
            searchTerm.setActive(resSet.getBoolean(3));
            // 5 old_start
            Timestamp oldStart = resSet.getTimestamp(5, Localization.UTC);
            if (!resSet.wasNull())
                searchTerm.setOldStart(new DateTime(oldStart.getTime()));

            // 6 interval length

            // Dirty hack: Because we cannot get every time via getTimestamp, we have to get the byte stream
            // and convert them into a string time.
            byte[] arr = resSet.getBytes(6);
            String intervalLength = new String(arr);
            searchTerm.setIntervalLength(
                    new Duration(Localization.DURATION_FORMATTER.parsePeriod(intervalLength).toStandardDuration()));

            // 7 priority
            searchTerm.setPriority(resSet.getInt(7));
            if (resSet.wasNull())
                searchTerm.setPriority(null);
            // 8 time_last_fetched
            Timestamp lastFetched = resSet.getTimestamp(8, Localization.UTC);
            if (!resSet.wasNull())
                searchTerm.setTimeLastFetched(new DateTime(lastFetched.getTime()));
            // 9 last_fetched_tweet_id
            Long lastFetchedTweetId = resSet.getLong(9);
            if (!resSet.wasNull())
                searchTerm.setLastFetchedTweetId(lastFetchedTweetId);
            // 10 last_fetched_tweet_count
            searchTerm.setLastFetchedTweetCount(resSet.getInt(10));
            if (resSet.wasNull())
                searchTerm.setLastFetchedTweetCount(null);
            //  11 when_created
            Timestamp when_created = resSet.getTimestamp(11, Localization.UTC);
            searchTerm.setWhenCreated(new DateTime(when_created.getTime()));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException sqlEx) {
            sqlEx.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (resSet != null)
                resSet.close();

            if (prepStatement != null)
                prepStatement.close();

            if (conn != null)
                conn.close();
        }

        return searchTerm;
    }

    /**
     * Inserts the search term into the database, if the term does not already existent.
     * During insertion, a new unique Id will be generated for this search term,
     * which can be retrieved by the corresponding method.
     * 
     * @param term The term to be inserted into the database.
     * @throws SQLException Thrown when there is a problem during insertion.
     */
    public static void insert(SearchTerm term) throws SQLException {
        Connection conn = null;
        PreparedStatement prepStatement = null;
        ResultSet resSet = null;

        String sqlInsertTerm = "INSERT INTO search_terms ( term, active, current_start, old_start, interval_length,  priority, time_last_fetched, last_fetched_tweet_id, last_fetched_tweet_count, when_created)"
                + "VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";

        FileInputStream fis = null;

        try {
            Properties props = new Properties();
            fis = new FileInputStream(DATABASE_PROPERTY_PATH);
            props.load(fis);
            Class.forName(props.getProperty("javabase.jdbc.driver"));

            String dbUrl = props.getProperty("javabase.jdbc.url") + "?user="
                    + props.getProperty("javabase.jdbc.username") + "&password="
                    + props.getProperty("javabase.jdbc.password");
            conn = DriverManager.getConnection(dbUrl);

            // Insert search term into DB
            prepStatement = conn.prepareStatement(sqlInsertTerm);
            prepStatement.setString(1, term.getTerm()); // name
            prepStatement.setBoolean(2, term.isActive()); // active
            prepStatement.setTimestamp(3, new Timestamp(term.getCurrentStart().getMillis()), Localization.UTC); // current_start
            prepStatement.setTimestamp(4, new Timestamp(term.getOldStart().getMillis()), Localization.UTC); // old_start
            prepStatement.setString(5, Localization.printDuration(term.getIntervalLength())); // Interval_Length

            if (term.getPriority() != null) // priority
                prepStatement.setInt(6, term.getPriority());
            else
                prepStatement.setNull(6, java.sql.Types.NULL);

            if (term.getTimeLastFetched() == null) // time_last_fetched
                prepStatement.setNull(7, java.sql.Types.NULL);
            else
                prepStatement.setTimestamp(7, new Timestamp(term.getTimeLastFetched().getMillis()),
                        Localization.UTC);

            if (term.getTimeLastFetched() == null) // last_fetched_tweet_id
                prepStatement.setNull(8, java.sql.Types.NULL);
            else
                prepStatement.setLong(8, term.getLastFetchedTweetId());

            if (term.getTimeLastFetched() == null) // last_fetched_tweet_count
                prepStatement.setNull(9, java.sql.Types.NULL);
            else
                prepStatement.setInt(9, term.getLastFetchedTweetCount());
            prepStatement.setTimestamp(10, new Timestamp(term.getWhenCreated().getMillis()), Localization.UTC); // when_started

            prepStatement.execute();
            prepStatement.close();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException sqlEx) {
            sqlEx.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (resSet != null)
                resSet.close();

            if (prepStatement != null)
                prepStatement.close();

            if (conn != null)
                conn.close();
        }
    }

    /**
     * Returns whether this search term is considered new.
     * A new Term has neither a old start nor a last fetched
     * tweet id.
     * @return Whether this search term is considered new.
     */
    public boolean isNew() {
        return _old_start == null && _lastFetchedTweetId == null;
    }

    /**
     * Compares if search_term object is the same as this. 
     * Parameter to check equality is the Name of the Search Term
     * @param obj The other SearchTerm object to be compared.
     * @return Return true, if the names match, false otherwise.
     */
    @Override
    public boolean equals(Object obj) {
        SearchTerm other = (SearchTerm) obj;

        return _term.equals(other._term);
    }

    /**
     * Returns a unique hashcode for this object.
     * Used internally by Java to create HashMaps
     */
    @Override
    public int hashCode() {
        int hash = HashCodeUtil.SEED;
        return HashCodeUtil.hash(hash, _id);
    }

    /**
     * Checks all search terms and returns only these we consider searchable. Starts a new iteration if the 
     * updatecurrentstart is set to true. A searchable term if it is either a) in the middle of a iteration
     * or b) we have waited long enough(>= interval length).
     *  
     * @param updateCurrentStart set true if we should also start a new iteration if possible
     * @return  true if we are in the middle of a active iteration or its has waited long enough
     *          false else
     *          
     */
    public boolean isSearchable(boolean updateCurrentStart) {
        // Check if we are in a iteration
        if (getLastFetchedTweetId() == null) {
            // We are not in the middle of an iteration
            if (getOldStart() != null) {
                // We have an old start date, so we now have to check if we
                // have waited long enough
                DateTime oldStart = getOldStart();
                DateTime now = new DateTime();

                // Check for interval length constraint
                if ((now.getMillis() - oldStart.getMillis()) >= getIntervalLength().getMillis()) {
                    // Start a new iteration if updatecurrentstart set to true
                    if (updateCurrentStart) {
                        setCurrentStart(now);
                    }
                    return true;
                }
                // else: we haven't waited long enough
            } else {
                // This is the very first iteration of the search term, so add it regardless of
                // interval length
                return true;
            }
        } else { // We are in the middle of an iteration, so add the search term
            return true;
        }
        // failed all checks, not searchable
        return false;
    }
}