package net.sf.appstatus.batch.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import net.sf.appstatus.core.batch.IBatch;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
*
* <pre>
* <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
* scope="singleton">
* <constructor-arg ref="dataSource" />
* </bean>
*
* <bean id="batchDao" class="net.sf.appstatus.batch.jdbc.BatchDao"
* scope="singleton"> <property name="jdbcTemplate" ref="jdbcTemplate" />
* </bean>
*
*
*
* <bean id="jdbcBatchManager"
* class="net.sf.appstatus.batch.jdbc.JdbcBatchManager" scope="singleton">
* <property name="batchDao" ref="batchDao" />
* </bean>
*
* </pre>
*
* Create table: BATCH
*
* <p>
* <table>
* <tr>
* <td>UUID_BATCH</td>
* <td>varchar (256)</td>
* </tr>
* <tr>
* <td>GROUP_BATCH</td>
* <td>varchar (256)</td>
* </tr>
* <tr>
* <td>NAME_BATCH</td>
* <td>varchar (256)</td>
* </tr>
* <tr>
* <td>START_DATE</td>
* <td>DATETIME</td>
* </tr>
* <tr>
* <td>END_DATE</td>
* <td>DATETIME</td>
* </tr>
* <tr>
* <td>UPDATED</td>
* <td>DATETIME</td>
* </tr>
* <tr>
* <td>STATUS</td>
* <td>varchar (64)</td>
* </tr>
* <tr>
* <td>SUCCESS</td>
* <td>BOOLEAN</td>
* </tr>
* <tr>
* <td>ITEMCOUNT</td>
* <td>LONG</td>
* </tr>
* <tr>
* <td>ITEM</td>
* <td>varchar (256)</td>
* </tr>
* </tr>
* <tr>
* <td>CURRENT_TASK</td>
* <td>varchar (256)</td>
* </tr>
* <tr>
* <td>PROGRESS</td>
* <td>Float</td>
* </tr>
* <tr>
* <td>REJECT</td>
* <td>CLOB</td>
* </tr>
* <tr>
* <td>LAST_MSG</td>
* <td>varchar (1024)</td>
* </tr>
* </table>
*/
public class BatchDao {
// Query codes
public static final int BATCH_FETCH = 2;
public static final int BATCH_DELETE = 3;
public static final int BATCH_DELETE_OLD = 4;
public static final int BATCH_DELETE_SUCCESS = 5;
public static final int BATCH_INSERT = 6;
public static final int BATCH_UPDATE = 7;
public static final int BATCH_CREATE_TABLE = 1;
public static final int BATCH_FETCH_BY_NAME = 8;
private static Logger logger = LoggerFactory.getLogger(BatchDao.class);
/**
* Spring JDBC template
*/
private JdbcTemplate jdbcTemplate;
protected String tableName = "BATCH";
/**
* Check for storage table and create if necessary.
*
* @return true if database was created.
*/
public boolean createDbIfNecessary() {
logger.info("Looking for table {}...", tableName);
try {
this.jdbcTemplate.execute("select count(*) from " + tableName);
logger.info("Table {} found.", tableName);
return false;
} catch (DataAccessException e) {
logger.warn("Table {} not found. Creating using \"{}\" ...",
tableName, getSql(BATCH_CREATE_TABLE));
jdbcTemplate.execute(getSql(BATCH_CREATE_TABLE));
logger.info("Table {} created", tableName);
return true;
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteBatch(final String uuidBatch) {
Object[] parameters = new Object[] { uuidBatch };
this.jdbcTemplate.update(getSql(BATCH_DELETE), parameters);
logger.info("Batch {} deleted.", uuidBatch);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteOldBatches(final int delay) {
Object[] parameters = new Object[] {
new DateTime().minusMonths(delay).toDate(),
IBatch.STATUS_RUNNING };
this.jdbcTemplate.update(getSql(BATCH_DELETE_OLD), parameters);
logger.info("Batchs older than {} months deleted.", delay);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteSuccessBatches() {
Object[] parameters = new Object[] { IBatch.STATUS_SUCCESS };
this.jdbcTemplate.update(getSql(BATCH_DELETE_SUCCESS), parameters);
logger.info("Batchs with success status deleted.");
}
@Transactional(readOnly = true)
private List<BdBatch> fetchBdBatch(final int max, String[] status) {
SqlRowSet srs = this.jdbcTemplate.queryForRowSet(
insertParametersFromList(getSql(BATCH_FETCH), status),
new Object[] { max });
return resultSet2Batches(srs);
}
@Transactional(readOnly = true)
private List<BdBatch> fetchBdBatch(String group, String name,
final int max, String[] status) {
SqlRowSet srs = this.jdbcTemplate.queryForRowSet(
insertParametersFromList(getSql(BATCH_FETCH_BY_NAME), status),
new Object[] { group, name, max });
return resultSet2Batches(srs);
}
@Transactional(readOnly = true)
public List<BdBatch> fetchError(final int max) {
return fetchBdBatch(max, new String[] { IBatch.STATUS_FAILURE });
}
@Transactional(readOnly = true)
public List<BdBatch> fetchFinished(final int max) {
return fetchBdBatch(max, new String[] { IBatch.STATUS_SUCCESS,
IBatch.STATUS_FAILURE });
}
@Transactional(readOnly = true)
public List<BdBatch> fetchRunning(final int max) {
return fetchBdBatch(max, new String[] { IBatch.STATUS_RUNNING });
}
/**
* Get SQL query for the requested action.
* <p>
* Override this method to adapt to a new SQL Dialect.
*
* @param query
* {@link #BATCH_FETCH} {@link #BATCH_CREATE_TABLE}
* @return the SQL query
*/
protected String getSql(int query) {
switch (query) {
case BATCH_UPDATE:
return "UPDATE "
+ tableName
+ " set ITEM = ?, CURRENT_TASK = ?, END_DATE=?, GROUP_BATCH=?, ITEMCOUNT=?, "
+ "LAST_MSG = ?, UPDATED=?, NAME_BATCH=?, PROGRESS = ?, REJECT = ?, STATUS=?, "
+ "SUCCESS=? WHERE UUID_BATCH=?";
case BATCH_DELETE_SUCCESS:
return "delete from " + tableName
+ " where STATUS = ? AND REJECT ='' ";
case BATCH_DELETE:
return "delete from " + tableName + " where UUID_BATCH = ?";
case BATCH_DELETE_OLD:
return "delete from " + tableName
+ " where UPDATED < ? AND STATUS != ?";
case BATCH_INSERT:
return "INSERT into "
+ tableName
+ " (UUID_BATCH,GROUP_BATCH,NAME_BATCH,START_DATE,STATUS,ITEMCOUNT) values (?,?,?,?,?,0)";
case BATCH_FETCH:
return "SELECT UUID_BATCH, ITEM, CURRENT_TASK, END_DATE, GROUP_BATCH, ITEMCOUNT, LAST_MSG, UPDATED,"
+ " NAME_BATCH, PROGRESS, REJECT, START_DATE, STATUS,SUCCESS FROM "//
+ tableName
+ " WHERE STATUS IN ( %s ) ORDER BY UPDATED DESC LIMIT ? ";
case BATCH_FETCH_BY_NAME:
return "SELECT UUID_BATCH, ITEM, CURRENT_TASK, END_DATE, GROUP_BATCH, ITEMCOUNT, LAST_MSG, UPDATED,"
+ " NAME_BATCH, PROGRESS, REJECT, START_DATE, STATUS,SUCCESS FROM "//
+ tableName
+ " WHERE GROUP_BATCH = ? AND NAME_BATCH = ? AND STATUS IN ( %s ) ORDER BY UPDATED DESC LIMIT ? ";
case BATCH_CREATE_TABLE:
return "CREATE TABLE " + tableName + " (" //
+ " UUID_BATCH varchar(256) NOT NULL," //
+ "GROUP_BATCH varchar(256) NULL," //
+ "NAME_BATCH varchar(256) NULL," //
+ "START_DATE DATETIME NULL," //
+ "END_DATE DATETIME NULL," //
+ "UPDATED DATETIME NULL," //
+ "STATUS varchar(64) NULL," //
+ "SUCCESS BOOLEAN NULL," //
+ "ITEMCOUNT BIGINT NULL," //
+ "ITEM varchar(256) NULL," //
+ "CURRENT_TASK varchar(256) NULL," //
+ "PROGRESS Float NULL," //
+ "REJECT CLOB NULL," //
+ "LAST_MSG varchar(1024) NULL," //
+ "PRIMARY KEY (UUID_BATCH)" + ") ";
default:
return null;
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public BdBatch save(BdBatch bdBatch) {
Object[] parameters = new Object[] { bdBatch.getUuid(),
bdBatch.getGroup(), bdBatch.getName(), bdBatch.getStartDate(),
bdBatch.getStatus() };
logger.debug("PARAMETERS UUID BATCH:{} NAME: {} GROUP: {}",
bdBatch.getUuid(), bdBatch.getName(), bdBatch.getGroup());
int result = this.jdbcTemplate.update(getSql(BATCH_INSERT), parameters);
logger.debug("{} lines inserted.", result);
return bdBatch;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void update(BdBatch bdBatch) {
logger.debug("Batch {} update ", bdBatch.getUuid());
Object[] parameters = new Object[] { bdBatch.getCurrentItem(),
bdBatch.getCurrentTask(), bdBatch.getEndDate(),
bdBatch.getGroup(), bdBatch.getItemCount(),
bdBatch.getLastMessage(), bdBatch.getLastUpdate(),
bdBatch.getName(), bdBatch.getProgress(), bdBatch.getReject(),
bdBatch.getStatus(), bdBatch.getSuccess(), bdBatch.getUuid() };
this.jdbcTemplate.update(getSql(BATCH_UPDATE), parameters);
}
@Transactional(readOnly = true)
public List<BdBatch> fetch(String group, String name, int max) {
return fetchBdBatch(group, name, max, new String[] {
IBatch.STATUS_SUCCESS, IBatch.STATUS_FAILURE });
}
private List<BdBatch> resultSet2Batches(SqlRowSet srs) {
List<BdBatch> results = new ArrayList<BdBatch>();
while (srs.next()) {
BdBatch bdBatch;
try {
bdBatch = mappinpBdbatch(srs);
results.add(bdBatch);
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return results;
}
/**
* Replace %s by values passed as parameter
*
* @param values
* @return String
*/
private String insertParametersFromList(String sql, String[] values) {
for (int i = 0; i < values.length; ++i) {
values[i] = String.format("'%s'", values[i]);
}
return String.format(sql, StringUtils.join(values, ","));
}
/**
* Read batch object from result set.
*
* @param srs
* @return
* @throws IOException
* @throws SQLException
*/
private BdBatch mappinpBdbatch(SqlRowSet srs) throws SQLException,
IOException {
BdBatch bdBatch = new BdBatch();
bdBatch.setUuid(srs.getString("UUID_BATCH"));
bdBatch.setCurrentItem(srs.getString("ITEM"));
bdBatch.setEndDate(srs.getDate("END_DATE"));
bdBatch.setGroup(srs.getString("GROUP_BATCH"));
bdBatch.setItemCount(srs.getLong("ITEMCOUNT"));
bdBatch.setLastMessage(srs.getString("LAST_MSG"));
bdBatch.setLastUpdate(srs.getDate("UPDATED"));
bdBatch.setName(srs.getString("NAME_BATCH"));
bdBatch.setProgress(srs.getFloat("PROGRESS"));
bdBatch.setStartDate(srs.getDate("START_DATE"));
bdBatch.setStatus(srs.getString("STATUS"));
bdBatch.setSuccess(srs.getBoolean("SUCCESS"));
// Clob
Clob reject = (Clob) srs.getObject("REJECT");
bdBatch.setReject(clobToString(reject));
return bdBatch;
}
private String clobToString(Clob clob) throws SQLException, IOException {
if (clob == null)
return null;
InputStream in = clob.getAsciiStream();
Reader read = new InputStreamReader(in);
StringWriter w = new StringWriter();
int c = -1;
while ((c = read.read()) != -1) {
w.write(c);
}
w.flush();
return StringUtils.trim(w.toString());
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}