package org.wso2.carbon.humantask.core.dao.jpa.openjpa;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.humantask.core.HumanTaskConstants;
import org.wso2.carbon.humantask.core.api.scheduler.InvalidJobsInDbException;
import org.wso2.carbon.humantask.core.dao.*;
import org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.*;
import org.wso2.carbon.humantask.core.dao.jpa.openjpa.util.HumanTaskBuilderImpl;
import org.wso2.carbon.humantask.core.dao.sql.HumanTaskJPQLQueryBuilder;
import org.wso2.carbon.humantask.core.deployment.HumanTaskDeploymentUnit;
import org.wso2.carbon.humantask.core.engine.HumanTaskException;
import org.wso2.carbon.humantask.core.engine.runtime.api.HumanTaskIllegalArgumentException;
import org.wso2.carbon.humantask.core.engine.runtime.api.HumanTaskRuntimeException;
import org.wso2.carbon.humantask.core.engine.util.CommonTaskUtil;
import org.wso2.carbon.humantask.core.internal.HumanTaskServiceComponent;
import org.wso2.carbon.humantask.core.scheduler.NotificationScheduler;
import org.wso2.carbon.humantask.core.store.HumanTaskBaseConfiguration;
import org.wso2.carbon.humantask.core.store.TaskConfiguration;
import org.wso2.carbon.utils.CarbonUtils;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.File;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
/**
* The Open JPA based implementation of HumanTaskDAOConnection interface.
*/
public class HumanTaskDAOConnectionImpl implements HumanTaskDAOConnection {
private static final Log log = LogFactory.getLog(HumanTaskDAOConnectionImpl.class);
/**
* The entity manager handling object persistence
*/
private EntityManager entityManager;
private NotificationScheduler notificationScheduler;
/**
* @param entityManager : The entity manager handling object persistence.
*/
public HumanTaskDAOConnectionImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
public TaskDAO createTask(TaskCreationContext creationContext)
throws HumanTaskException {
HumanTaskBuilderImpl taskBuilder = new HumanTaskBuilderImpl();
if (log.isDebugEnabled()) {
log.debug("Creating task instance for task " + creationContext.getTaskConfiguration().getName());
}
MessageDAO inputMessage = createMessage(creationContext);
inputMessage.setMessageType(MessageDAO.MessageType.INPUT);
taskBuilder.addTaskCreationContext(creationContext)
.addInputMessage(inputMessage);
TaskDAO task = taskBuilder.build();
entityManager.persist(task);
try {
creationContext.injectExpressionEvaluationContext(task);
JPATaskUtil.processGenericHumanRoles(task, creationContext.getTaskConfiguration(),
creationContext.getPeopleQueryEvaluator(), creationContext.getEvalContext());
JPATaskUtil.processPresentationElements(task, creationContext.getTaskConfiguration(),
creationContext);
if (task.getType().equals(TaskType.TASK)) {
CommonTaskUtil.nominate(task, creationContext.getPeopleQueryEvaluator());
} else if (task.getType().equals(TaskType.NOTIFICATION)) {
task.setStatus(TaskStatus.READY);
notificationScheduler = HumanTaskServiceComponent.getHumanTaskServer().getTaskEngine().getNotificationScheduler();
notificationScheduler.checkForNotificationTasks(creationContext.getTaskConfiguration(), creationContext, task);
}
task.setPriority(CommonTaskUtil.calculateTaskPriority(creationContext.getTaskConfiguration(),
creationContext.getEvalContext()));
CommonTaskUtil.setTaskToMessage(task);
if (TaskType.TASK.equals(task.getType())) {
CommonTaskUtil.processDeadlines(task,
(TaskConfiguration) creationContext.getTaskConfiguration(),
creationContext.getEvalContext());
CommonTaskUtil.scheduleDeadlines(task);
}
//Setting HumanTask context override attributes
CommonTaskUtil.setTaskOverrideContextAttributes(task, creationContext.getMessageHeaderParts());
HumanTaskServiceComponent.getHumanTaskServer().getTaskEngine().getEventProcessor().processEvent(CommonTaskUtil.createNewTaskEvent(task));
} catch (HumanTaskRuntimeException ex) {
String errorMsg = "Error occurred after task creation for Task ID: " + task.getId() + ". Cause: " + ex.getMessage();
log.error(errorMsg);
task.setStatus(TaskStatus.ERROR);
throw ex;
}
return task;
}
public TaskDAO getTask(Long taskId) {
TaskDAO matchingTask = entityManager.find(Task.class, taskId);
if (matchingTask != null) {
//lazy loading items.
matchingTask.getHumanRoles();
matchingTask.getParentTask();
matchingTask.getInputMessage();
matchingTask.getOutputMessage();
matchingTask.getFailureMessage();
matchingTask.getComments();
matchingTask.getAttachments();
matchingTask.getPresentationDescriptions();
matchingTask.getPresentationNames();
matchingTask.getPresentationSubjects();
matchingTask.getPresentationParameters();
matchingTask.getEvents();
} else {
String errorMsg = "Task lookup failed for task id " + taskId + " invalid task id provide";
log.error(errorMsg);
throw new HumanTaskIllegalArgumentException(errorMsg);
}
return matchingTask;
}
public List<TaskDAO> getTasksForUser(String userName) {
return null;
}
public MessageDAO createMessage(TaskCreationContext taskCreationContext) {
Message message = new Message();
MessageHelper msgHelper = new MessageHelper(message);
return msgHelper.createMessage(taskCreationContext);
}
public MessageDAO createMessage() {
return new Message();
}
public OrganizationalEntityDAO createNewOrgEntityObject(String userName,
OrganizationalEntityDAO.OrganizationalEntityType type) {
OrganizationalEntityDAO orgEntity = new OrganizationalEntity();
orgEntity.setName(userName);
orgEntity.setOrgEntityType(type);
return orgEntity;
}
public GenericHumanRoleDAO createNewGHRObject(GenericHumanRoleDAO.GenericHumanRoleType type) {
GenericHumanRoleDAO ghr = new GenericHumanRole();
ghr.setType(type);
return ghr;
}
public void obsoleteTasks(String taskName, Integer tenantId) {
entityManager.createQuery("UPDATE org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.Task t SET t.status = :statusToSet WHERE t.name = :taskName AND t.tenantId = :tenantId").
//entityManager.createNamedQuery(Task.OBSOLETE_TASKS).
setParameter("taskName", taskName).
setParameter("tenantId", tenantId).
setParameter("statusToSet", TaskStatus.OBSOLETE).executeUpdate();
}
public List<TaskDAO> simpleTaskQuery(SimpleQueryCriteria simpleQueryCriteria) {
Query taskQuery = entityManager.createQuery("SELECT t FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.Task t WHERE t.status <> :obsoleteStatus ").
setParameter("obsoleteStatus", TaskStatus.OBSOLETE);
return taskQuery.getResultList();
}
public List<TaskDAO> searchTasks(SimpleQueryCriteria queryCriteria) {
HumanTaskJPQLQueryBuilder queryBuilder = new HumanTaskJPQLQueryBuilder(queryCriteria, entityManager);
Query taskQuery = queryBuilder.build();
return taskQuery.getResultList();
}
public int getTasksCount(SimpleQueryCriteria queryCriteria) {
HumanTaskJPQLQueryBuilder queryBuilder = new HumanTaskJPQLQueryBuilder(queryCriteria, entityManager);
Query countQuery = queryBuilder.buildCount();
int firstResult = Integer.parseInt(countQuery.getSingleResult().toString());
return firstResult;
}
public void removeTasks(SimpleQueryCriteria queryCriteria) {
HumanTaskJPQLQueryBuilder queryBuilder = new HumanTaskJPQLQueryBuilder(queryCriteria, entityManager);
Query taskQuery = queryBuilder.build();
taskQuery.executeUpdate();
}
public CommentDAO getCommentDAO(String commentString, String commentedByUserName) {
return new Comment(commentString, commentedByUserName);
}
/**
* Create a Deadline
*
* @return DeadLine
*/
public DeadlineDAO createDeadline() {
return new Deadline();
}
/**
* Create a new job
*
* @return HumanTaskJobDAO
*/
public HumanTaskJobDAO createHumanTaskJobDao() {
// entityManager.merge(humanTaskJob);
return new HumanTaskJob();
}
/**
* Return a list of unique nodes identifiers found in the database. This is used
* to initialize the list of known nodes when a new node starts up.
*
* @return list of unique node identfiers found in the databaseuniqu
*/
public List<String> getNodeIds() {
Query q = entityManager.createQuery("SELECT DISTINCT t.nodeId FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t WHERE t.nodeId IS NOT NULL");
return (List<String>) q.getResultList();
}
/**
* "Dequeue" jobs from the database that are ready for immediate execution; this basically
* is a select/delete operation with constraints on the nodeId and scheduled time.
*
* @param nodeId node identifier of the jobs
* @param maxtime only jobs with scheduled time earlier than this will be dequeued
* @param maxjobs maximum number of jobs to deqeue
* @return list of jobs that met the criteria and were deleted from the database
*/
public List<HumanTaskJobDAO> dequeueImmediate(String nodeId, long maxtime, int maxjobs) {
Query q = entityManager.createQuery("SELECT DISTINCT t FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t WHERE t.nodeId = ?1 AND t.time < ?2 order by t.time");
q.setParameter(1, nodeId);
q.setParameter(2, maxtime);
q.setMaxResults(maxjobs);
return (List<HumanTaskJobDAO>) q.getResultList();
}
/**
* Assign a particular node identifier to a fraction of jobs in the database that do not have one,
* and are up for execution within a certain time. Only a fraction of the jobs found are assigned
* the node identifier. This fraction is determined by the "y" parameter, while membership in the
* group (of jobs that get the nodeId) is determined by the "x" parameter. Essentially the logic is:
* <code>
* UPDATE jobs AS job
* WHERE job.scheduledTime before :maxtime
* AND job.nodeId is null
* AND job.scheduledTime MOD :y == :x
* SET job.nodeId = :nodeId
* </code>
*
* @param nodeId node identifier to assign to jobs
* @param x the result of the mod-division
* @param y the dividend of the mod-division
* @param maxtime only jobs with scheduled time earlier than this will be updated
* @return number of jobs updated
*/
public int updateAssignToNode(String nodeId, int x, int y, long maxtime) {
Query q = entityManager.createQuery("UPDATE org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t SET t.nodeId = ?1 WHERE t.nodeId IS NULL AND MOD(t.time, ?2) = ?3 and t.time < ?4");
q.setParameter(1, nodeId);
q.setParameter(2, y);
q.setParameter(3, x);
q.setParameter(4, maxtime);
return q.executeUpdate();
}
/**
* Reassign jobs from one node to another.
*
* @param oldnode node assigning from
* @param newnode new node asssigning to
* @return number of rows changed
*/
public int updateReassign(String oldnode, String newnode) {
Query q = entityManager.createQuery("UPDATE org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t SET t.nodeId = ?1, t.scheduled = 0 WHERE t.nodeId = ?2");
q.setParameter(1, newnode);
q.setParameter(2, oldnode);
return q.executeUpdate();
}
public int deleteAllJobs() {
Query q = entityManager.createQuery("DELETE FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob");
return q.executeUpdate();
}
public boolean delete(String jobId, String nodeId) {
Query q = entityManager.createQuery("DELETE FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t WHERE t.id = ?1 AND t.nodeId = ?2");
q.setParameter(1, jobId);
q.setParameter(2, nodeId);
return q.executeUpdate() == 1;
}
/**
* Delete jobs for the specified task and returns the list of jobIds
*
* @param taskId Task Id
* @return List of job ids corresponding to the deleted jobs
*/
public List<Long> deleteJobsForTask(Long taskId) {
Query q = entityManager.createQuery("SELECT t.id FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t WHERE t.taskId = ?1");
q.setParameter(1, taskId);
List<Long> jobIds = (List<Long>) q.getResultList();
q = entityManager.createQuery("DELETE FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t WHERE t.taskId = ?1");
q.setParameter(1, taskId);
q.executeUpdate();
return jobIds;
}
/**
* Update the schedule time for a job
*
* @param taskId Task ID
* @param immediate Whether the job should be executed immediately
* @param nearFuture Whether the job should be executed in the near future
* @param nodeId Node ID
* @param time Time to be updated
* @param name Name of the task
* @return If there is a job, corresponding to taskId and name, returns the job id. If the are
* no jobs then returns -1.
* @throws org.wso2.carbon.humantask.core.api.scheduler.InvalidJobsInDbException If there are two or more
* jobs are selected for the Task Id
*/
public Long updateJob(Long taskId, String name, boolean immediate, boolean nearFuture,
String nodeId, Long time) throws InvalidJobsInDbException {
Query q = entityManager.createQuery("SELECT t.id FROM org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t WHERE t.taskId = ?1 AND t.name = ?2");
q.setParameter(1, taskId);
q.setParameter(2, name);
List<Long> jobIds = (List<Long>) q.getResultList();
if (jobIds.size() != 1) {
return handleException(taskId, name, jobIds.size());
}
int size;
if (immediate) {
log.info("Immediate");
// Immediate scheduling means we add the job immediately to the todo list and
// we put it in the DB for safe keeping
q = entityManager.createQuery("UPDATE org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t SET t.time = ?1, t.nodeId = ?2, t.scheduled = true WHERE t.taskId = ?3 AND t.name = ?4");
q.setParameter(1, time);
q.setParameter(2, nodeId);
q.setParameter(3, taskId);
q.setParameter(4, name);
} else if (nearFuture) {
log.info("near");
// Near future, assign the job to ourselves (why? -- this makes it very unlikely that we
// would get two nodes trying to process the same instance
q = entityManager.createQuery("UPDATE org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t SET t.time = ?1, t.nodeId = ?2, t.scheduled = false WHERE t.taskId = ?3 AND t.name = ?4");
q.setParameter(1, time);
q.setParameter(2, nodeId);
q.setParameter(3, taskId);
q.setParameter(4, name);
} else {
log.info("far");
// Not the near future, we don't assign a node-id, we'll assign it later.
q = entityManager.createQuery("UPDATE org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.HumanTaskJob t SET t.time = ?1,t.nodeId = null, t.scheduled = false WHERE t.taskId = ?2 AND t.name = ?3");
q.setParameter(1, time);
q.setParameter(2, taskId);
q.setParameter(3, name);
}
size = q.executeUpdate();
if (size != 1) {
return handleException(taskId, name, size);
}
return jobIds.get(0);
}
private Long handleException(Long taskId, String name, int size)
throws InvalidJobsInDbException {
String errMsg;
if (size == 0) {
errMsg = "There are no any jobs, corresponding to Task ID: " + taskId +
" and Name: " + name;
log.warn(errMsg);
return -1L;
} else {
errMsg = "More than 1 (" + size + ") jobs are selected to Task ID: " +
taskId + " and Name: " + name;
log.error(errMsg);
throw new InvalidJobsInDbException(errMsg);
}
}
public EntityManager getEntityManager() {
return entityManager;
}
/**
* Creates a new EventDAO object.
*
* @return :
*/
public EventDAO createNewEventObject(TaskDAO task) {
EventDAO eventDAO = new Event();
eventDAO.setTimeStamp(new Date());
eventDAO.setOldState(task.getStatus());
return eventDAO;
}
public AttachmentDAO createAttachment() {
return new Attachment();
}
public DeploymentUnitDAO createDeploymentUnit() {
return null;
}
public long getNextVersion() {
List<TaskVersionDAO> resultList = entityManager.createQuery("select v from TaskVersion v")
.getResultList();
if (resultList.size() == 0) {
return 1;
} else {
TaskVersionDAO versionDAO = resultList.get(0);
return versionDAO.getTaskVersion() + 1;
}
}
public long setNextVersion(long version) {
List<TaskVersionDAO> resultList = entityManager.createQuery("select v from TaskVersion v").getResultList();
TaskVersionDAO versionDAO;
if (resultList.size() == 0) {
versionDAO = new TaskVersion();
} else {
versionDAO = resultList.get(0);
}
versionDAO.setTaskVersion(version);
entityManager.persist(versionDAO);
return version;
}
public DeploymentUnitDAO createDeploymentUnitDAO(HumanTaskDeploymentUnit deploymentUnit, int tenantId) {
String taskRelativePath = "repository" + File.separator + HumanTaskConstants.HUMANTASK_REPO_DIRECTORY +
File.separator + tenantId + File.separator + deploymentUnit.getName();
DeploymentUnitDAO deploymentUnitDAO = new DeploymentUnit();
deploymentUnitDAO.setVersion(deploymentUnit.getVersion());
deploymentUnitDAO.setName(deploymentUnit.getName());
deploymentUnitDAO.setPackageName(deploymentUnit.getPackageName());
deploymentUnitDAO.setStatus(TaskPackageStatus.ACTIVE);
deploymentUnitDAO.setChecksum(deploymentUnit.getMd5sum());
deploymentUnitDAO.setDeploymentUnitDir(taskRelativePath);
deploymentUnitDAO.setDeployDate(new Date());
deploymentUnitDAO.setTenantId(tenantId);
entityManager.persist(deploymentUnitDAO);
return deploymentUnitDAO;
}
public DeploymentUnitDAO getDeploymentUnit(int tenantId, String md5sum) {
Query q = entityManager.createQuery("select hu from org.wso2.carbon.humantask.core.dao.jpa.openjpa.model" +
".DeploymentUnit hu WHERE hu.status=?1 and hu.tenantId=?2 and hu.checksum=?3");
q.setParameter(1, TaskPackageStatus.ACTIVE);
q.setParameter(2, tenantId);
q.setParameter(3, md5sum);
List<DeploymentUnit> resultList = q.getResultList();
DeploymentUnit deploymentUnit = null;
if (resultList.size() != 0) {
deploymentUnit = resultList.get(0);
}
return deploymentUnit;
}
/**
* Obtain the list of deployment units matching the given package name. Name of the deployment unit will be
* appended with the version while package name will remain the same. Hence if multiple version of the same package exists
* (At least one version of the human task package is already deployed, there should be a maching deployment unit )
*
* @param tenantId
* @param packageName
* @return
*/
public List<DeploymentUnitDAO> getDeploymentUnitsForPackageName(int tenantId, String packageName) {
Query query = entityManager.createQuery("SELECT hdu from org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.DeploymentUnit" +
" hdu WHERE hdu.packageName=?1 and hdu.tenantId=?2 ");
query.setParameter(1, packageName);
query.setParameter(2, tenantId);
return query.getResultList();
}
public DeploymentUnitDAO getDeploymentUnitByName(int tenantId, String packageName) {
Query q = entityManager.createQuery("select hu from org.wso2.carbon.humantask.core.dao.jpa.openjpa.model" +
".DeploymentUnit hu WHERE hu.status=?1 and hu.tenantId=?2 and hu.packageName=?3");
q.setParameter(1, TaskPackageStatus.ACTIVE);
q.setParameter(2, tenantId);
q.setParameter(3, packageName);
List<DeploymentUnit> resultList = q.getResultList();
DeploymentUnit deploymentUnit = null;
if (resultList.size() != 0) {
deploymentUnit = resultList.get(0);
}
return deploymentUnit;
}
public List<TaskDAO> getMatchingTaskInstances(String packageName, int tenantId) {
Query q = entityManager.createQuery("select t from org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.Task t where t.packageName = ?1 and t.tenantId = ?2");
q.setParameter(1, packageName);
q.setParameter(2, tenantId);
return q.getResultList();
}
public void deleteDeploymentUnits(String packageName, int tenantId) {
Query query = entityManager.createQuery("delete from org.wso2.carbon.humantask.core.dao.jpa.openjpa.model.DeploymentUnit d where d.packageName = ?1 and d.tenantId = ?2");
query.setParameter(1, packageName);
query.setParameter(2, tenantId);
query.executeUpdate();
}
}