package org.jbpm.process.audit;
import static org.jbpm.query.jpa.data.QueryParameterIdentifiersUtil.*;
import static org.kie.internal.query.QueryParameterIdentifiers.CORRELATION_KEY_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.DATE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.DURATION_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.END_DATE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.EXTERNAL_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.IDENTITY_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.LAST_VARIABLE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.NODE_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.NODE_INSTANCE_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.NODE_NAME_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.TYPE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.OLD_VALUE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.OUTCOME_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.PROCESS_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.PROCESS_INSTANCE_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.PROCESS_INSTANCE_PARENT_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.PROCESS_INSTANCE_STATUS_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.PROCESS_NAME_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.PROCESS_VERSION_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.START_DATE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.VALUE_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.VARIABLE_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.VARIABLE_INSTANCE_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.VAR_VALUE_ID_LIST;
import static org.kie.internal.query.QueryParameterIdentifiers.VAR_VAL_SEPARATOR;
import static org.kie.internal.query.QueryParameterIdentifiers.WORK_ITEM_ID_LIST;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.SingularAttribute;
import org.jbpm.query.jpa.data.QueryCriteria;
import org.jbpm.query.jpa.data.QueryWhere;
import org.jbpm.query.jpa.data.QueryWhere.QueryCriteriaType;
import org.jbpm.query.jpa.impl.QueryCriteriaUtil;
public class AuditQueryCriteriaUtil extends QueryCriteriaUtil {
// Query Field Info -----------------------------------------------------------------------------------------------------------
public final static Map<Class, Map<String, Attribute>> criteriaAttributes
= new ConcurrentHashMap<Class, Map<String, Attribute>>();
@Override
protected synchronized boolean initializeCriteriaAttributes() {
if( ProcessInstanceLog_.correlationKey == null ) {
// EMF/persistence has not been initialized:
// When a persistence unit (EntityManagerFactory) is initialized,
// the fields of classes annotated with @StaticMetamodel are filled using reflection
return false;
}
// do not do initialization twice (slow performance, otherwise it doesn't matter)
if( ! criteriaAttributes.isEmpty() ) {
return true;
}
// ProcessInstanceLog
addCriteria(criteriaAttributes, PROCESS_INSTANCE_ID_LIST, ProcessInstanceLog_.processInstanceId);
addCriteria(criteriaAttributes, PROCESS_ID_LIST, ProcessInstanceLog_.processId);
addCriteria(criteriaAttributes, START_DATE_LIST, ProcessInstanceLog_.start);
addCriteria(criteriaAttributes, END_DATE_LIST, ProcessInstanceLog_.end);
addCriteria(criteriaAttributes, PROCESS_INSTANCE_STATUS_LIST, ProcessInstanceLog_.status);
addCriteria(criteriaAttributes, PROCESS_INSTANCE_PARENT_ID_LIST, ProcessInstanceLog_.parentProcessInstanceId);
addCriteria(criteriaAttributes, OUTCOME_LIST, ProcessInstanceLog_.outcome);
addCriteria(criteriaAttributes, DURATION_LIST, ProcessInstanceLog_.duration);
addCriteria(criteriaAttributes, IDENTITY_LIST, ProcessInstanceLog_.identity);
addCriteria(criteriaAttributes, PROCESS_VERSION_LIST, ProcessInstanceLog_.processVersion);
addCriteria(criteriaAttributes, PROCESS_NAME_LIST, ProcessInstanceLog_.processName);
addCriteria(criteriaAttributes, CORRELATION_KEY_LIST, ProcessInstanceLog_.correlationKey);
addCriteria(criteriaAttributes, EXTERNAL_ID_LIST, ProcessInstanceLog_.externalId);
// NodeInstanceLog
addCriteria(criteriaAttributes, PROCESS_INSTANCE_ID_LIST, NodeInstanceLog_.processInstanceId);
addCriteria(criteriaAttributes, PROCESS_ID_LIST, NodeInstanceLog_.processId);
addCriteria(criteriaAttributes, EXTERNAL_ID_LIST, NodeInstanceLog_.externalId);
addCriteria(criteriaAttributes, DATE_LIST, NodeInstanceLog_.date);
addCriteria(criteriaAttributes, NODE_INSTANCE_ID_LIST, NodeInstanceLog_.nodeInstanceId);
addCriteria(criteriaAttributes, NODE_ID_LIST, NodeInstanceLog_.nodeId);
addCriteria(criteriaAttributes, NODE_NAME_LIST, NodeInstanceLog_.nodeName);
addCriteria(criteriaAttributes, TYPE_LIST, NodeInstanceLog_.nodeType);
addCriteria(criteriaAttributes, WORK_ITEM_ID_LIST, NodeInstanceLog_.workItemId);
// Var
addCriteria(criteriaAttributes, PROCESS_INSTANCE_ID_LIST, VariableInstanceLog_.processInstanceId);
addCriteria(criteriaAttributes, PROCESS_ID_LIST, VariableInstanceLog_.processId);
addCriteria(criteriaAttributes, DATE_LIST, VariableInstanceLog_.date);
addCriteria(criteriaAttributes, EXTERNAL_ID_LIST, VariableInstanceLog_.externalId);
addCriteria(criteriaAttributes, VARIABLE_INSTANCE_ID_LIST, VariableInstanceLog_.variableInstanceId);
addCriteria(criteriaAttributes, VARIABLE_ID_LIST, VariableInstanceLog_.variableId);
addCriteria(criteriaAttributes, VALUE_LIST, VariableInstanceLog_.value);
addCriteria(criteriaAttributes, OLD_VALUE_LIST, VariableInstanceLog_.oldValue);
return true;
}
// Implementation specific logic ----------------------------------------------------------------------------------------------
protected JPAService jpaService;
public AuditQueryCriteriaUtil(JPAService service) {
super(criteriaAttributes);
this.jpaService = service;
}
/**
* This protected constructor is used in the kie-remote-services module
*
* @param criteriaAttributes
* @param service
*/
protected AuditQueryCriteriaUtil(Map<Class, Map<String, Attribute>> criteriaAttributes, JPAService service) {
super(criteriaAttributes);
this.jpaService = service;
}
protected EntityManager getEntityManager() {
return this.jpaService.getEntityManager();
}
protected Object joinTransaction(EntityManager em) {
return this.jpaService.joinTransaction(em);
}
protected void closeEntityManager(EntityManager em, Object transaction) {
this.jpaService.closeEntityManager(em, transaction);
}
// Implementation specific methods --------------------------------------------------------------------------------------------
protected CriteriaBuilder getCriteriaBuilder() {
return getEntityManager().getCriteriaBuilder();
}
@Override
protected <T> List<T> createQueryAndCallApplyMetaCriteriaAndGetResult(QueryWhere queryWhere, CriteriaQuery<T> criteriaQuery, CriteriaBuilder builder) {
EntityManager em = getEntityManager();
Object newTx = joinTransaction(em);
Query query = em.createQuery(criteriaQuery);
applyMetaCriteriaToQuery(query, queryWhere);
// execute query
List<T> result = query.getResultList();
closeEntityManager(em, newTx);
return result;
}
@Override
@SuppressWarnings("unchecked")
protected <R,T> Predicate implSpecificCreatePredicateFromSingleCriteria(
CriteriaQuery<R> query,
CriteriaBuilder builder,
Class queryType,
QueryCriteria criteria,
QueryWhere queryWhere) {
Root<?> table = getRoot(query, queryType);
return variableInstanceLogSpecificCreatePredicateFromSingleCriteria(query, builder, criteria, table);
}
@SuppressWarnings("unchecked")
public static <Q,T> Predicate variableInstanceLogSpecificCreatePredicateFromSingleCriteria(
CriteriaQuery<Q> query,
CriteriaBuilder builder,
QueryCriteria criteria,
Root<T> table) {
Predicate predicate;
if( LAST_VARIABLE_LIST.equals(criteria.getListId()) ) {
Subquery<VariableInstanceLog> maxIdSubQuery = query.subquery(VariableInstanceLog.class);
Root from = maxIdSubQuery.from(VariableInstanceLog.class);
maxIdSubQuery.select(builder.max(from.get(VariableInstanceLog_.id)));
maxIdSubQuery.groupBy(
from.get(VariableInstanceLog_.variableId),
from.get(VariableInstanceLog_.processInstanceId));
Attribute varIdField = VariableInstanceLog_.id;
// TODO: add the current group's criteria list to the subquery,
// in order to make the subquery more efficient
// -- but that requires making the criteria list available here as an argument.. :/
Expression expr;
if( varIdField instanceof SingularAttribute ) {
expr = table.get((SingularAttribute<T,?>)varIdField);
} else {
throw new IllegalStateException("Unexpected " + varIdField.getClass().getName() + " when processing last variable query criteria!");
}
predicate = builder.in(expr).value(maxIdSubQuery);
} else if( VAR_VALUE_ID_LIST.equals(criteria.getListId()) ) {
assert criteria.getValues().size() == 1 : "Only 1 var id/value parameter expected!";
// extract var/val information from criteria
Object varVal = criteria.getValues().get(0);
String [] parts = ((String) varVal).split(VAR_VAL_SEPARATOR, 2);
String varId = parts[1].substring(0,Integer.parseInt(parts[0]));
String val = parts[1].substring(Integer.parseInt(parts[0])+1);
// create predicates
SingularAttribute varVarIdField = VariableInstanceLog_.variableId;
Path varVarIdPath = table.get(varVarIdField);
SingularAttribute varValField = VariableInstanceLog_.value;
Path varValIdPath = table.get(varValField);
Predicate varIdPredicate = builder.equal(varVarIdPath, varId);
Predicate valPredicate;
if( QueryCriteriaType.REGEXP.equals(criteria.getType()) ) {
val = convertRegexToJPALikeExpression(val);
valPredicate = builder.like(varValIdPath, val);
} else {
valPredicate = builder.equal(varValIdPath, val);
}
// intersect predicates
predicate = builder.and(varIdPredicate, valPredicate);
} else {
throw new IllegalStateException("List id [" + getQueryParameterIdNameMap().get(Integer.parseInt(criteria.getListId()))
+ "] is not supported for queries on " + table.getJavaType().getSimpleName() + ".");
}
return predicate;
}
}