package ee.telekom.workflow.core.workunit;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.stereotype.Repository;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstanceStatus;
import ee.telekom.workflow.graph.WorkItemStatus;
import ee.telekom.workflow.util.AbstractWorkflowEngineDao;
/**
* Provides the engine's central polling query.
*
* @author Christian Klock
*/
@Repository
public class WorkUnitDao extends AbstractWorkflowEngineDao{
// The poll query does not change so we can store it as a constant
private String sqlCache;
private String cachedSqlClusterName;
/**
* Retrieves the list of work units that can be performed at the given date.
*/
public List<WorkUnit> findNewWorkUnits( Date now, String clusterName ){
if( sqlCache == null || !ObjectUtils.equals( cachedSqlClusterName, clusterName ) ){
sqlCache = getSql( clusterName );
cachedSqlClusterName = clusterName;
}
Object[] args = {now};
return getJdbcTemplate().query( sqlCache, args, WorkUnitRowMapper.INSTANCE );
}
/*
* Note: The (a) sub-query is written deliberately before the sub-queries (b) and (c).
* (b) and (c) return null values for woit_ref_num and waiting_since. PostgreSQL is only able to
* determine the type of this fields if there is a preceding sub-query (that is (a) in our case)
* that returns a non-null value for that field.
* If (b) and (c) were preceding (a), than PostgreSQL would use VARCHAR as a fallback type for the
* null value fields. This would make the UNION of the sub-queries fail due to a type mismatch.
*/
private String getSql( String clusterName ){
String clusterCondition = getClusterCondition( clusterName );
String sql = ""
// (a) completing signals, tasks, human tasks
+ "SELECT woin.ref_num AS woin_ref_num, "
+ " '" + WorkType.COMPLETE_WORK_ITEM.name() + "' AS type, "
+ " woit.ref_num AS woit_ref_num, "
+ " woit.date_updated AS waiting_since "
+ " FROM " + getSchema() + "work_items woit "
+ " JOIN " + getSchema() + "workflow_instances woin "
+ " ON woin.ref_num = woit.woin_ref_num "
+ " WHERE woin.status = '" + WorkflowInstanceStatus.EXECUTING.name() + "' "
+ " AND woin.locked = 'N' "
+ clusterCondition
+ " AND woit.status = '" + WorkItemStatus.EXECUTED.name() + "' "
+ " AND woit.due_date IS NULL "
+ "UNION ALL "
// (b) starting workflows
+ "SELECT woin.ref_num AS woin_ref_num, "
+ " '" + WorkType.START_WORKFLOW.name() + "' AS type, "
+ " null AS woit_ref_num, "
+ " null AS waiting_since "
+ " FROM " + getSchema() + "workflow_instances woin "
+ " WHERE woin.status = '" + WorkflowInstanceStatus.NEW.name() + "' "
+ " AND woin.locked = 'N' "
+ clusterCondition
+ " UNION ALL "
// (c) aborting workflows
+ "SELECT woin.ref_num AS woin_ref_num, "
+ " '" + WorkType.ABORT_WORKFLOW.name() + "' AS type, "
+ " null AS woit_ref_num, "
+ " null AS waiting_since "
+ " FROM " + getSchema() + "workflow_instances woin "
+ " WHERE woin.status = '" + WorkflowInstanceStatus.ABORT.name() + "' "
+ " AND woin.locked = 'N' "
+ clusterCondition
+ " UNION ALL "
// (d) completing timers
+ "SELECT woin.ref_num AS woin_ref_num, "
+ " '" + WorkType.COMPLETE_WORK_ITEM.name() + "' AS type, "
+ " woit.ref_num AS woit_ref_num, "
+ " woit.due_date AS waiting_since "
+ " FROM " + getSchema() + "work_items woit "
+ " JOIN " + getSchema() + "workflow_instances woin "
+ " ON woin.ref_num = woit.woin_ref_num "
+ " WHERE woin.status = '" + WorkflowInstanceStatus.EXECUTING.name() + "' "
+ " AND woin.locked = 'N' "
+ clusterCondition
+ " AND woit.status = '" + WorkItemStatus.NEW.name() + "' "
+ " AND woit.due_date < ? "
+ "UNION ALL "
// (e) executing tasks
+ "SELECT woin.ref_num AS woin_ref_num, "
+ " '" + WorkType.EXECUTE_TASK.name() + "' AS type, "
+ " woit.ref_num AS woit_ref_num, "
+ " COALESCE(woit.date_updated, woit.date_created) AS waiting_since "
+ " FROM " + getSchema() + "work_items woit "
+ " JOIN " + getSchema() + "workflow_instances woin "
+ " ON woin.ref_num = woit.woin_ref_num "
+ " WHERE woin.status = '" + WorkflowInstanceStatus.EXECUTING.name() + "' "
+ " AND woin.locked = 'N' "
+ clusterCondition
+ " AND woit.status = '" + WorkItemStatus.NEW.name() + "' "
+ " AND NOT woit.bean IS NULL "
+ " ORDER BY woin_ref_num ASC, waiting_since ASC NULLS FIRST, woit_ref_num ASC";
return sql.replaceAll( "\\s+", " " );
}
private static String getClusterCondition( String clusterName ){
if( clusterName == null || clusterName.trim().isEmpty() ){
return " AND woin.cluster_name IS NULL ";
}
else{
return " AND woin.cluster_name = '" + StringEscapeUtils.escapeSql( clusterName ) + "'";
}
}
}