/*
* Copyright (c) 2012, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.
*/
package org.wso2.carbon.humantask.core.scheduler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.humantask.*;
import org.wso2.carbon.humantask.core.api.scheduler.Scheduler;
import org.wso2.carbon.humantask.core.dao.*;
import org.wso2.carbon.humantask.core.engine.HumanTaskException;
import org.wso2.carbon.humantask.core.engine.runtime.ExpressionEvaluationContext;
import org.wso2.carbon.humantask.core.engine.runtime.api.EvaluationContext;
import org.wso2.carbon.humantask.core.engine.runtime.api.HumanTaskRuntimeException;
import org.wso2.carbon.humantask.core.internal.HumanTaskServerHolder;
import org.wso2.carbon.humantask.core.internal.HumanTaskServiceComponent;
import org.wso2.carbon.humantask.core.store.HumanTaskBaseConfiguration;
import org.wso2.carbon.humantask.core.store.TaskConfiguration;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.UserRealm;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import javax.xml.namespace.QName;
import java.util.*;
/**
* Implements JobProcessor for - Deadlines
*/
public class JobProcessorImpl implements Scheduler.JobProcessor {
private static Log log = LogFactory.getLog(JobProcessorImpl.class);
/**
* Implements execution of the job.
*
* @param jobInfo the job information
* @throws org.wso2.carbon.humantask.core.api.scheduler.Scheduler.JobProcessorException
*
*/
public void onScheduledJob(Scheduler.JobInfo jobInfo) throws Scheduler.JobProcessorException {
log.info("Executing Deadline: " + jobInfo.getName() + " :: " + jobInfo.getTaskId() +
" :: " + jobInfo.getJobId() + " :: " + jobInfo.getType());
try {
switch (jobInfo.getType()) {
case TIMER_DEADLINE:
executeDeadline(jobInfo.getTaskId(), jobInfo.getName());
break;
case TIMER_SUSPEND:
executeSuspend(jobInfo.getTaskId());
break;
}
} catch (Exception ex) {
throw new Scheduler.JobProcessorException(ex);
}
}
private void executeSuspend(long taskId) {
log.info("ON SUSPEND: task : " + taskId);
//TODO
}
private void executeDeadline(long taskId, String name) throws HumanTaskException {
//TODO what if two deadlines fired at the same time???
//TODO do the needful for deadlines. i.e create notifications and re-assign
log.info("ON DEADLINE: " + " : now: " + new Date());
TaskDAO task = HumanTaskServiceComponent.getHumanTaskServer().getDaoConnectionFactory().
getConnection().getTask(taskId);
// Setting the tenant id and tenant domain
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(task.getTenantId());
String tenantDomain = null;
try {
tenantDomain = HumanTaskServiceComponent.getRealmService().getTenantManager().getDomain(task.getTenantId());
} catch (UserStoreException e) {
log.error(" Cannot find the tenant domain " + e.toString());
}
if(tenantDomain == null) {
tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
}
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain);
TaskConfiguration taskConf = (TaskConfiguration) HumanTaskServiceComponent.getHumanTaskServer().
getTaskStoreManager().getHumanTaskStore(task.getTenantId()).
getTaskConfiguration(QName.valueOf(task.getName()));
TDeadline deadline = taskConf.getDeadline(name);
EvaluationContext evalCtx = new ExpressionEvaluationContext(task, taskConf);
List<TEscalation> validEscalations = new ArrayList<TEscalation>();
boolean reassingnmentAdded = false;
for (TEscalation escalation : deadline.getEscalationArray()) {
if (!escalation.isSetCondition()) {
//We only need the first Re-assignment and we ignore all other re-assignments
if (escalation.isSetReassignment() && !reassingnmentAdded) {
reassingnmentAdded = true;
} else if (escalation.isSetReassignment()) {
continue;
}
validEscalations.add(escalation);
continue;
}
if (evaluateCondition(escalation.getCondition().newCursor().getTextValue(),
escalation.getCondition().getExpressionLanguage() == null ?
taskConf.getExpressionLanguage() :
escalation.getCondition().
getExpressionLanguage(), evalCtx)) {
if (escalation.isSetReassignment() && !reassingnmentAdded) {
reassingnmentAdded = true;
} else if (escalation.isSetReassignment()) {
continue;
}
validEscalations.add(escalation);
}
}
//We may do this in the above for loop as well
for (TEscalation escalation : validEscalations) {
if (log.isDebugEnabled()) {
log.debug("Escalation: " + escalation.getName());
}
if (escalation.isSetLocalNotification() || escalation.isSetNotification()) {
QName qName;
if (escalation.isSetLocalNotification()) {
qName = escalation.getLocalNotification().getReference();
} else {
qName = new QName(taskConf.getName().getNamespaceURI(),
escalation.getNotification().getName());
}
HumanTaskBaseConfiguration notificationConfiguration = HumanTaskServiceComponent.
getHumanTaskServer().getTaskStoreManager().
getHumanTaskStore(task.getTenantId()).getActiveTaskConfiguration(qName);
if(notificationConfiguration == null) {
log.error("Fatal Error, notification definition not found for name " + qName.toString());
return;
}
TaskCreationContext taskContext = new TaskCreationContext();
taskContext.setTaskConfiguration(notificationConfiguration);
taskContext.setTenantId(task.getTenantId());
taskContext.setPeopleQueryEvaluator(HumanTaskServiceComponent.getHumanTaskServer().getTaskEngine().
getPeopleQueryEvaluator());
Map<String, Element> tempBodyParts = new HashMap<String, Element>();
Map<String, Element> tempHeaderParts = new HashMap<String, Element>();
QName tempName = null;
TToParts toParts = escalation.getToParts();
if (toParts == null) {
//get the input message of the task
MessageDAO msg = task.getInputMessage();
tempName = msg.getName();
for (Map.Entry<String, Element> partEntry : msg.getBodyParts().entrySet()) {
tempBodyParts.put(partEntry.getKey(), partEntry.getValue());
}
for (Map.Entry<String, Element> partEntry : msg.getHeaderParts().entrySet()) {
tempHeaderParts.put(partEntry.getKey(), partEntry.getValue());
}
taskContext.setMessageBodyParts(tempBodyParts);
taskContext.setMessageHeaderParts(tempHeaderParts);
taskContext.setMessageName(tempName);
} else {
for (TToPart toPart : toParts.getToPartArray()) {
if (!notificationConfiguration.isValidPart(toPart.getName())) {
//This validation should be done at the deployment time
String errMsg = "The part: " + toPart.getName() + " is not available" +
" in the corresponding WSDL message";
log.error(errMsg);
throw new RuntimeException(errMsg);
}
String expLang = toPart.getExpressionLanguage() == null ?
taskConf.getExpressionLanguage() : toPart.getExpressionLanguage();
Node nodePart = HumanTaskServerHolder.getInstance().getHtServer().getTaskEngine().
getExpressionLanguageRuntime(expLang).evaluateAsPart(
toPart.newCursor().getTextValue(),
toPart.getName(),
evalCtx);
tempBodyParts.put(toPart.getName(), (Element) nodePart);
}
}
taskContext.setMessageBodyParts(tempBodyParts);
taskContext.setMessageHeaderParts(tempHeaderParts);
taskContext.setMessageName(tempName);
HumanTaskServerHolder.getInstance().getHtServer().getTaskEngine().
getDaoConnectionFactory().getConnection().createTask(taskContext);
} else { //if re-assignment
if (escalation.getReassignment().getPotentialOwners().isSetFrom()) {
escalation.getReassignment().getPotentialOwners().getFrom().getArgumentArray();
String roleName = null;
for (TArgument argument : escalation.getReassignment().getPotentialOwners().
getFrom().getArgumentArray()) {
if ("role".equals(argument.getName())) {
roleName = argument.newCursor().getTextValue().trim();
}
}
if (roleName == null) {
String errMsg = "Value for argument name 'role' is expected.";
log.error(errMsg);
throw new Scheduler.JobProcessorException(errMsg);
}
if (!isExistingRole(roleName, task.getTenantId())) {
log.warn("Role name " + roleName + " does not exist for tenant id" + task.getTenantId());
}
List<OrganizationalEntityDAO> orgEntities = new ArrayList<OrganizationalEntityDAO>();
OrganizationalEntityDAO orgEntity = HumanTaskServiceComponent.getHumanTaskServer().
getDaoConnectionFactory().getConnection().
createNewOrgEntityObject(roleName,
OrganizationalEntityDAO.OrganizationalEntityType.GROUP);
orgEntities.add(orgEntity);
task.replaceOrgEntitiesForLogicalPeopleGroup(
GenericHumanRoleDAO.GenericHumanRoleType.POTENTIAL_OWNERS,
orgEntities);
} else {
String errMsg = "From element is expected inside the assignment";
log.error(errMsg);
throw new Scheduler.JobProcessorException(errMsg);
}
}
}
}
private boolean evaluateCondition(String exp, String expLang, EvaluationContext evalCtx) {
return HumanTaskServerHolder.getInstance().getHtServer().getTaskEngine().
getExpressionLanguageRuntime(expLang).evaluateAsBoolean(exp, evalCtx);
}
// Checks the particular role name exists.
private boolean isExistingRole(String roleName, int tenantId) {
RegistryService registryService = HumanTaskServiceComponent.getRegistryService();
try {
UserRealm userRealm = registryService.getUserRealm(tenantId);
return userRealm.getUserStoreManager().isExistingRole(roleName);
} catch (Exception e) {
throw new HumanTaskRuntimeException("Cannot retrieve user realm for tenantId :" + tenantId, e);
}
}
}