package fr.acxio.tools.agia.alfresco;
/*
* Copyright 2014 Acxio
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ReaderNotOpenException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.util.Assert;
import fr.acxio.tools.agia.alfresco.dao.NodeDao;
import fr.acxio.tools.agia.alfresco.dao.NodeDaoException;
import fr.acxio.tools.agia.alfresco.domain.Node;
import fr.acxio.tools.agia.alfresco.domain.NodeStatus;
import fr.acxio.tools.agia.common.ProcessIndicatorItemWrapper;
/**
* <p>
* Item reader for hibernate stored nodes.
* </p>
* <p>
* Item readers for hibernate use to lack efficiency because the whole objects
* trees is stored into the step context.</br> The HibernateNodeReader will read
* nodes' ids only, and a dedicated processor will retrieve the nodes
* themselves.
* </p>
* <p>
* The {@code currentStep} property represents the node lifecycle steps.</br> It
* can take any integer value, and is used to select the nodes at the given
* step.</br> By convention, positives values represent successful steps and
* negatives ones represent failed steps.</br> Some default values are provided
* by {@link fr.acxio.tools.agia.alfresco.domain.NodeStatus NodeStatus}.
* </p>
* <p>
* The {@code sqlQuery} property can be altered with extreme care to get a
* specific collection of nodes.
* </p>
*
* @author pcollardez
*
*/
public class HibernateNodeReader implements ItemReader<ProcessIndicatorItemWrapper<Node>>, StepExecutionListener, InitializingBean, DisposableBean {
private static final Logger LOGGER = LoggerFactory.getLogger(HibernateNodeReader.class);
private static final String JOBSTEP_PARAM = "jobStep";
/**
* Thread monitor
*/
private final Object lock = new Object();
/**
* Initialization flag
*/
private volatile boolean initialized = false;
/**
* Collection of nodes' IDs
*/
private volatile Iterator<Long> keys;
private NamedParameterJdbcTemplate jdbcTemplate;
private NodeDao nodeDao;
/**
* SQL query used to retrieve nodes' IDs
*/
private String sqlQuery = "SELECT node_id FROM xalfnode WHERE parent_id IS NULL AND jobStep=:jobStep ORDER BY node_id";
/**
* Lifecyle step of the nodes to retrieve
*/
private int currentStep = NodeStatus.NEW;
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public void setNodeDao(NodeDao sNodeDao) {
nodeDao = sNodeDao;
}
/**
* Sets the SQL query. USE WITH EXTREME CARE.
*
* @param sSqlQuery
* the new SQL query.
*/
public void setSqlQuery(String sSqlQuery) {
sqlQuery = sSqlQuery;
}
/**
* Sets the lifecyle step of the nodes to retrieve
*
* @param sCurrentStep
*/
public void setCurrentStep(int sCurrentStep) {
currentStep = sCurrentStep;
}
public void destroy() {
initialized = false;
keys = null;
}
public void afterPropertiesSet() {
Assert.notNull(jdbcTemplate, "You must provide a DataSource.");
Assert.notNull(nodeDao, "You must provide a NodeDao.");
}
/**
* Retrieves nodes' IDs
*
* @return a collection of IDs
*/
private List<Long> retrieveKeys() {
synchronized (lock) {
MapSqlParameterSource aParams = new MapSqlParameterSource();
aParams.addValue(JOBSTEP_PARAM, currentStep);
return jdbcTemplate.query(sqlQuery, aParams, new ParameterizedRowMapper<Long>() {
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getLong(1);
}
});
}
}
public ProcessIndicatorItemWrapper<Node> read() throws NodeDaoException {
if (!initialized) {
throw new ReaderNotOpenException("Reader must be open before it can be used.");
}
Long id = null;
synchronized (lock) {
if (keys.hasNext()) {
id = keys.next();
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Retrieved key from list: " + id);
}
if (id == null) {
return null;
}
return new ProcessIndicatorItemWrapper<Node>(id, nodeDao.findById(id));
}
public void beforeStep(StepExecution sStepExecution) {
synchronized (lock) {
if (keys == null) {
keys = retrieveKeys().iterator();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Keys obtained for staging.");
}
initialized = true;
}
}
}
public ExitStatus afterStep(StepExecution sStepExecution) {
synchronized (lock) {
initialized = false;
keys = null;
}
return ExitStatus.COMPLETED;
}
}