net.solarnetwork.node.dao.jdbc.reactor.JdbcInstructionDao.java Source code

Java tutorial

Introduction

Here is the source code for net.solarnetwork.node.dao.jdbc.reactor.JdbcInstructionDao.java

Source

/* ==================================================================
 * JdbcInstructionDao.java - Feb 28, 2011 3:11:51 PM
 * 
 * Copyright 2007-2011 SolarNetwork.net Dev Team
 * 
 * This program 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 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ==================================================================
 * $Id$
 * ==================================================================
 */

package net.solarnetwork.node.dao.jdbc.reactor;

import static net.solarnetwork.node.dao.jdbc.JdbcDaoConstants.SCHEMA_NAME;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import net.solarnetwork.node.dao.jdbc.AbstractJdbcDao;
import net.solarnetwork.node.reactor.Instruction;
import net.solarnetwork.node.reactor.InstructionStatus;
import net.solarnetwork.node.reactor.InstructionStatus.InstructionState;
import net.solarnetwork.node.reactor.support.BasicInstruction;
import net.solarnetwork.node.reactor.support.BasicInstructionStatus;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * JDBC implementation of {@link JdbcInstructionDao}.
 * 
 * @author matt
 * @version $Revision$
 */
public class JdbcInstructionDao extends AbstractJdbcDao<Instruction>
        implements net.solarnetwork.node.reactor.InstructionDao {

    /** The default tables version. */
    public static final int DEFAULT_TABLES_VERSION = 1;

    /** The table name for {@link Instruction} data. */
    public static final String TABLE_INSTRUCTION = "sn_instruction";

    /** The table name for {@link Instruction} parameter data. */
    public static final String TABLE_INSTRUCTION_PARAM = "sn_instruction_param";

    /** The table name for {@link InstructionStatus} data. */
    public static final String TABLE_INSTRUCTION_STATUS = "sn_instruction_status";

    /** The default classpath Resource for the {@code initSqlResource}. */
    public static final String DEFAULT_INIT_SQL = "derby-instruction-init.sql";

    /** The default value for the {@code sqlGetTablesVersion} property. */
    public static final String DEFAULT_SQL_GET_TABLES_VERSION = "SELECT svalue FROM solarnode.sn_settings WHERE skey = '"
            + SCHEMA_NAME + '.' + TABLE_INSTRUCTION + ".version'";

    /**
     * The classpath Resource for the SQL template for inserting an Instruction.
     */
    public static final String RESOURCE_SQL_INSERT_INSTRUCTION = "insert";

    /**
     * The classpath Resource for the SQL template for inserting an Instruction
     * parameter.
     */
    public static final String RESOURCE_SQL_INSERT_INSTRUCTION_PARAM = "insert-param";

    /**
     * The classpath Resource for the SQL template for inserting an
     * InstructionStatus.
     */
    public static final String RESOURCE_SQL_INSERT_INSTRUCTION_STATUS = "insert-status";

    /**
     * The classpath Resource for the SQL template for updating an
     * InstructionStatus.
     */
    public static final String RESOURCE_SQL_UPDATE_INSTRUCTION_STATUS = "update-status";

    /**
     * The classpath Resource for the SQL template for selecting Instruction by
     * unique keys.
     */
    public static final String RESOURCE_SQL_SELECT_INSTRUCTION_FOR_KEYS = "select-for-keys";

    /**
     * The classpath Resource for the SQL template for selecting Instruction by
     * ID.
     */
    public static final String RESOURCE_SQL_SELECT_INSTRUCTION_FOR_ID = "select-for-id";

    /**
     * The classpath Resource for the SQL template for selecting Instruction by
     * state.
     */
    public static final String RESOURCE_SQL_SELECT_INSTRUCTION_FOR_STATE = "select-for-state";

    /**
     * The classpath Resource for the SQL template for selecting Instruction by
     * state.
     */
    public static final String RESOURCE_SQL_SELECT_INSTRUCTION_FOR_ACKNOWEDGEMENT = "select-for-ack";

    /**
     * The classpath Resource for the SQL template for deleting old Instruction
     * rows.
     */
    public static final String RESOURCE_SQL_DELETE_OLD = "delete-old";

    /**
     * Default constructor.
     */
    public JdbcInstructionDao() {
        super();
        setTableName(TABLE_INSTRUCTION);
        setTablesVersion(DEFAULT_TABLES_VERSION);
        setSqlGetTablesVersion(DEFAULT_SQL_GET_TABLES_VERSION);
        setSqlResourcePrefix("derby-instruction");
        setInitSqlResource(new ByteArrayResource(getSqlResource("init").getBytes()));
    }

    @Override
    public String[] getTableNames() {
        return new String[] { getTableName(), TABLE_INSTRUCTION_PARAM, TABLE_INSTRUCTION_STATUS };
    }

    @Override
    @Transactional(readOnly = true, propagation = Propagation.REQUIRED)
    public Instruction getInstruction(Long instructionId) {
        return getJdbcTemplate().query(getSqlResource(RESOURCE_SQL_SELECT_INSTRUCTION_FOR_ID),
                new Object[] { instructionId }, new ResultSetExtractor<Instruction>() {

                    @Override
                    public Instruction extractData(ResultSet rs) throws SQLException, DataAccessException {
                        List<Instruction> results = extractInstructions(rs);
                        if (results.size() > 0) {
                            return results.get(0);
                        }
                        return null;
                    }
                });
    }

    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public Long storeInstruction(final Instruction instruction) {
        // first store our Instruction entity
        final Long pk = storeDomainObject(instruction, getSqlResource(RESOURCE_SQL_INSERT_INSTRUCTION));

        // now store all the Instruction's parameters
        getJdbcTemplate().execute(new PreparedStatementCreator() {

            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                PreparedStatement ps = con.prepareStatement(getSqlResource(RESOURCE_SQL_INSERT_INSTRUCTION_PARAM));
                return ps;
            }
        }, new PreparedStatementCallback<Object>() {

            @Override
            public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
                int pos = 0;
                for (String paramName : instruction.getParameterNames()) {
                    String[] paramValues = instruction.getAllParameterValues(paramName);
                    if (paramValues == null || paramValues.length < 1) {
                        continue;
                    }
                    for (String paramValue : paramValues) {
                        int col = 1;
                        ps.setLong(col++, pk);
                        ps.setLong(col++, pos);
                        ps.setString(col++, paramName);
                        ps.setString(col++, paramValue);
                        ps.addBatch();
                        pos++;
                    }
                }
                int[] batchResults = ps.executeBatch();
                if (log.isTraceEnabled()) {
                    log.trace("Batch inserted {} instruction params: {}", pos, Arrays.toString(batchResults));
                }
                return null;
            }
        });

        // finally crate a status row
        Date statusDate = new Date();
        getJdbcTemplate().update(getSqlResource(RESOURCE_SQL_INSERT_INSTRUCTION_STATUS), pk,
                new java.sql.Timestamp(statusDate.getTime()), InstructionState.Received.toString());
        return pk;
    }

    @Override
    protected void setStoreStatementValues(Instruction instruction, PreparedStatement ps) throws SQLException {
        int col = 1;
        ps.setTimestamp(col++, new java.sql.Timestamp(instruction.getInstructionDate().getTime()));
        ps.setString(col++, instruction.getRemoteInstructionId());
        ps.setString(col++, instruction.getInstructorId());
        ps.setString(col++, instruction.getTopic());
    }

    @Override
    @Transactional(readOnly = true, propagation = Propagation.REQUIRED)
    public Instruction getInstruction(String instructionId, String instructorId) {
        return getJdbcTemplate().query(getSqlResource(RESOURCE_SQL_SELECT_INSTRUCTION_FOR_KEYS),
                new Object[] { instructionId, instructorId }, new ResultSetExtractor<Instruction>() {

                    @Override
                    public Instruction extractData(ResultSet rs) throws SQLException, DataAccessException {
                        List<Instruction> results = extractInstructions(rs);
                        if (results.size() > 0) {
                            return results.get(0);
                        }
                        return null;
                    }
                });
    }

    private List<Instruction> extractInstructions(ResultSet rs) throws SQLException {
        List<Instruction> results = new ArrayList<Instruction>(5);
        BasicInstruction bi = null;
        while (rs.next()) {
            long currId = rs.getLong(1);
            if (bi == null || bi.getId().longValue() != currId) {
                InstructionStatus status = new BasicInstructionStatus(currId,
                        InstructionState.valueOf(rs.getString(6)), rs.getTimestamp(7),
                        (rs.getString(8) == null ? null : InstructionState.valueOf(rs.getString(8))));
                bi = new BasicInstruction(currId, rs.getString(2), rs.getTimestamp(3), rs.getString(4),
                        rs.getString(5), status);
                results.add(bi);
            }
            String pName = rs.getString(9);
            String pValue = rs.getString(10);
            if (pName != null) {
                bi.addParameter(pName, pValue);
            }
        }
        return results;
    }

    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public void storeInstructionStatus(Long instructionId, InstructionStatus status) {
        getJdbcTemplate().update(getSqlResource(RESOURCE_SQL_UPDATE_INSTRUCTION_STATUS),
                status.getInstructionState().toString(), (status.getAcknowledgedInstructionState() == null ? null
                        : status.getAcknowledgedInstructionState().toString()),
                instructionId);

    }

    @Override
    @Transactional(readOnly = true, propagation = Propagation.REQUIRED)
    public List<Instruction> findInstructionsForState(InstructionState state) {
        return getJdbcTemplate().query(getSqlResource(RESOURCE_SQL_SELECT_INSTRUCTION_FOR_STATE),
                new Object[] { state.toString() }, new ResultSetExtractor<List<Instruction>>() {

                    @Override
                    public List<Instruction> extractData(ResultSet rs) throws SQLException, DataAccessException {
                        return extractInstructions(rs);
                    }
                });
    }

    @Override
    @Transactional(readOnly = true, propagation = Propagation.REQUIRED)
    public List<Instruction> findInstructionsForAcknowledgement() {
        return getJdbcTemplate().query(getSqlResource(RESOURCE_SQL_SELECT_INSTRUCTION_FOR_ACKNOWEDGEMENT),
                new ResultSetExtractor<List<Instruction>>() {

                    @Override
                    public List<Instruction> extractData(ResultSet rs) throws SQLException, DataAccessException {
                        return extractInstructions(rs);
                    }
                });
    }

    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public int deleteHandledInstructionsOlderThan(final int hours) {
        return getJdbcTemplate().update(new PreparedStatementCreator() {

            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                final String sql = getSqlResource(RESOURCE_SQL_DELETE_OLD);
                log.debug("Preparing SQL to delete old instructions [{}] with hours [{}]", sql, hours);
                PreparedStatement ps = con.prepareStatement(sql);
                Calendar c = Calendar.getInstance();
                c.add(Calendar.HOUR, -hours);
                ps.setTimestamp(1, new Timestamp(c.getTimeInMillis()), c);
                return ps;
            }
        });
    }

}