/*
Copyright 2011-2014 Red Hat, Inc
This file is part of PressGang CCMS.
PressGang CCMS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PressGang CCMS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PressGang CCMS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jboss.pressgang.ccms.server.utils;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.ws.rs.core.MultivaluedMap;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.pressgang.ccms.filter.base.IFieldFilter;
import org.jboss.pressgang.ccms.filter.builder.TopicFilterQueryBuilder;
import org.jboss.pressgang.ccms.filter.utils.FilterUtilities;
import org.jboss.pressgang.ccms.model.BlobConstants;
import org.jboss.pressgang.ccms.model.Category;
import org.jboss.pressgang.ccms.model.Filter;
import org.jboss.pressgang.ccms.model.FilterCategory;
import org.jboss.pressgang.ccms.model.FilterField;
import org.jboss.pressgang.ccms.model.FilterLocale;
import org.jboss.pressgang.ccms.model.FilterTag;
import org.jboss.pressgang.ccms.model.IntegerConstants;
import org.jboss.pressgang.ccms.model.LanguageImage;
import org.jboss.pressgang.ccms.model.ProcessStatus;
import org.jboss.pressgang.ccms.model.ProcessType;
import org.jboss.pressgang.ccms.model.Project;
import org.jboss.pressgang.ccms.model.StringConstants;
import org.jboss.pressgang.ccms.model.Tag;
import org.jboss.pressgang.ccms.model.TagToPropertyTag;
import org.jboss.pressgang.ccms.model.Topic;
import org.jboss.pressgang.ccms.model.TopicToBugzillaBug;
import org.jboss.pressgang.ccms.model.TopicToPropertyTag;
import org.jboss.pressgang.ccms.model.TranslatedTopicData;
import org.jboss.pressgang.ccms.model.User;
import org.jboss.pressgang.ccms.model.contentspec.ContentSpecToProcess;
import org.jboss.pressgang.ccms.rest.v1.constants.CommonFilterConstants;
import org.jboss.pressgang.ccms.server.constants.Constants;
import org.jboss.pressgang.ccms.utils.common.CollectionUtilities;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EntityUtilities extends org.jboss.pressgang.ccms.filter.utils.EntityUtilities {
private static final Logger log = LoggerFactory.getLogger(EntityUtilities.class);
private static final List<ProcessStatus> EXECUTING_STATUSES = Arrays.asList(ProcessStatus.PENDING, ProcessStatus.QUEUED,
ProcessStatus.EXECUTING);
public static byte[] loadBlobConstant(final EntityManager entityManager, final Integer id) {
if (id == null) return null;
final BlobConstants constant = entityManager.find(BlobConstants.class, id);
if (constant == null) {
log.error("Expected to find a record in the BlobConstants table with an ID of " + id);
return null;
}
return constant.getConstantValue();
}
public static Integer loadIntegerConstant(final EntityManager entityManager, final Integer id) {
if (id == null) return null;
final IntegerConstants constant = entityManager.find(IntegerConstants.class, id);
if (constant == null) {
log.error("Expected to find a record in the IntegerConstants table with an ID of " + id);
return null;
}
return constant.getConstantValue();
}
public static String loadStringConstant(final EntityManager entityManager, final Integer id) {
if (id == null) return null;
final StringConstants constant = entityManager.find(StringConstants.class, id);
if (constant == null) {
log.error("Expected to find a record in the StringConstants table with an ID of " + id);
return null;
}
return constant.getConstantValue();
}
@SuppressWarnings("unchecked")
public static User getUserFromUsername(final EntityManager entityManager, final String username) {
if (username == null) return null;
final Query query = entityManager.createQuery(User.SELECT_ALL_QUERY + " where user.userName = '" + username + "'");
final List<User> users = query.getResultList();
return users == null ? null : (users.size() == 1 ? users.get(0) : null);
}
public static Filter populateFilter(final EntityManager entityManager, final MultivaluedMap<String, String> paramMap,
final String filterName, final String tagPrefix, final String groupTagPrefix, final String categoryInternalPrefix,
final String categoryExternalPrefix, final String localePrefix, final IFieldFilter fieldFilter) {
final Map<String, String> newParamMap = new HashMap<String, String>();
for (final String key : paramMap.keySet()) {
try {
newParamMap.put(key, paramMap.getFirst(key).replace("%25", "%"));
} catch (final Exception ex) {
log.warn("The URL query parameter " + key + " with value " + paramMap.getFirst(key) + " could not be URLDecoded", ex);
}
}
return populateFilter(entityManager, newParamMap, filterName, tagPrefix, groupTagPrefix, categoryInternalPrefix,
categoryExternalPrefix, localePrefix, fieldFilter);
}
protected static Map<String, String> decodeUrlParameters(final Map<String, String> params) {
final Map<String, String> newParamMap = new HashMap<String, String>();
for (Map.Entry<String, String> entry : params.entrySet()) {
try {
newParamMap.put(entry.getKey(), URLDecoder.decode(entry.getValue(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
log.warn("The URL query parameter " + entry.getKey() + " with value " + entry.getValue() + " could not be URLDecoded", e);
}
}
return newParamMap;
}
/**
* This function takes the url parameters and uses them to populate a Filter object
*/
public static Filter populateFilter(final EntityManager entityManager, final Map<String, String> paramMap, final String filterName,
final String tagPrefix, final String groupTagPrefix, final String categoryInternalPrefix, final String categoryExternalPrefix,
final String localePrefix, final IFieldFilter fieldFilter) {
// decode the url parameters
final Map<String, String> fixedParamMap = decodeUrlParameters(paramMap);
// attempt to get the filter id from the url
Integer filterId = null;
if (fixedParamMap.containsKey(filterName)) {
final String filterQueryParam = fixedParamMap.get(filterName);
try {
filterId = Integer.parseInt(filterQueryParam);
} catch (final Exception ex) {
// filter value was not an integer
filterId = null;
log.debug("The filter ID URL query parameter was not an integer. Got " + filterQueryParam + ". Probably a malformed URL.",
ex);
}
}
Filter filter = null;
/* First attempt to populate the filter from a filterID variable */
if (filterId != null) {
filter = entityManager.find(Filter.class, filterId);
}
/* If that fails, use the other URL params */
if (filter == null) {
filter = new Filter();
for (final String key : fixedParamMap.keySet()) {
final boolean tagVar = tagPrefix != null && key.startsWith(tagPrefix);
final boolean groupTagVar = groupTagPrefix != null && key.startsWith(groupTagPrefix);
final boolean catIntVar = categoryInternalPrefix != null && key.startsWith(categoryInternalPrefix);
final boolean catExtVar = categoryExternalPrefix != null && key.startsWith(categoryExternalPrefix);
final boolean localeVar = localePrefix != null && key.matches("^" + localePrefix + "\\d*$");
final String state = fixedParamMap.get(key);
// add the filter category states
if (catIntVar || catExtVar) {
/*
* get the category and project id data from the variable name
*/
final String catProjDetails = catIntVar ? key.replaceFirst(categoryInternalPrefix, "") : key.replaceFirst(
categoryExternalPrefix, "");
// split the category and project id out of the data
final String[] catProjID = catProjDetails.split("-");
/*
* some validity checks. make sure we have one or two strings after the split.
*/
if (catProjID.length != 1 && catProjID.length != 2) continue;
// try to get the category and project ids
Integer catID = null;
Integer projID = null;
try {
catID = Integer.parseInt(catProjID[0]);
/*
* if the array has just one element, we have only specified the category. in this case the project is
* the common project
*/
if (catProjID.length == 2) projID = Integer.parseInt(catProjID[1]);
} catch (final Exception ex) {
log.debug("Was expecting an integer. Got " + catProjID[0] + ". Probably a malformed URL.", ex);
continue;
}
// at this point we have found a url variable that
// contains a catgeory and project id
final Category category = entityManager.find(Category.class, catID);
final Project project = projID != null ? entityManager.find(Project.class, projID) : null;
Integer dbState;
if (catIntVar) {
if (state.equals(CommonFilterConstants.AND_LOGIC)) dbState = CommonFilterConstants.CATEGORY_INTERNAL_AND_STATE;
else dbState = CommonFilterConstants.CATEGORY_INTERNAL_OR_STATE;
} else {
if (state.equals(CommonFilterConstants.AND_LOGIC)) dbState = CommonFilterConstants.CATEGORY_EXTERNAL_AND_STATE;
else dbState = CommonFilterConstants.CATEGORY_EXTERNAL_OR_STATE;
}
final FilterCategory filterCategory = new FilterCategory();
filterCategory.setFilter(filter);
filterCategory.setProject(project);
filterCategory.setCategory(category);
filterCategory.setCategoryState(dbState);
filter.getFilterCategories().add(filterCategory);
}
// add the filter tag states
else if (tagVar) {
try {
final Integer tagId = Integer.parseInt(key.replaceFirst(tagPrefix, ""));
final Integer intState = Integer.parseInt(state);
// get the Tag object that the tag id represents
final Tag tag = entityManager.getReference(Tag.class, tagId);
if (tag != null) {
final FilterTag filterTag = new FilterTag();
filterTag.setTag(tag);
filterTag.setTagState(intState);
filterTag.setFilter(filter);
filter.getFilterTags().add(filterTag);
}
} catch (final Exception ex) {
log.debug("Probably an invalid tag query parameter. Parameter: " + key + " Value: " + state, ex);
}
} else if (groupTagVar) {
final Integer tagId = Integer.parseInt(key.replaceFirst(groupTagPrefix, ""));
// final Integer intState = Integer.parseInt(state);
// get the Tag object that the tag id represents
final Tag tag = entityManager.getReference(Tag.class, tagId);
if (tag != null) {
final FilterTag filterTag = new FilterTag();
filterTag.setTag(tag);
filterTag.setTagState(CommonFilterConstants.GROUP_TAG_STATE);
filterTag.setFilter(filter);
filter.getFilterTags().add(filterTag);
}
} else if (localeVar) {
try {
final String localeName = state.replaceAll("\\d", "");
final Integer intState = Integer.parseInt(state.replaceAll("[^\\d]", ""));
final FilterLocale filterLocale = new FilterLocale();
filterLocale.setLocaleName(localeName);
filterLocale.setLocaleState(intState);
filterLocale.setFilter(filter);
filter.getFilterLocales().add(filterLocale);
} catch (final Exception ex) {
log.debug("Probably an invalid locale query parameter. Parameter: " + key + " Value: " + state, ex);
}
}
// add the filter field states
else {
if (fieldFilter.hasFieldName(key)) {
final FilterField filterField = new FilterField();
filterField.setFilter(filter);
filterField.setField(key);
filterField.setValue(state);
filterField.setDescription(fieldFilter.getFieldDesc(key));
filter.getFilterFields().add(filterField);
}
}
}
}
return filter;
}
public static Filter populateFilter(final MultivaluedMap<String, String> paramMap, final IFieldFilter fieldFilter) {
return populateFilter(paramMap, fieldFilter);
}
public static Filter populateFilter(final EntityManager entityManager, final MultivaluedMap<String, String> paramMap,
final String localePrefix, final IFieldFilter fieldFilter) {
return populateFilter(entityManager, paramMap, null, null, null, null, null, localePrefix, fieldFilter);
}
/**
* @return A list of topic ids that have open bugzilla bugs assigned to them
*/
@SuppressWarnings("unchecked")
public static List<Integer> getTopicsWithOpenBugs(final EntityManager entityManager) {
final List<TopicToBugzillaBug> results = entityManager.createQuery(
TopicToBugzillaBug.SELECT_ALL_QUERY + " WHERE topicToBugzillaBug.bugzillaBug.bugzillaBugOpen = true").getResultList();
final List<Integer> retValue = new ArrayList<Integer>();
for (final TopicToBugzillaBug map : results)
retValue.add(map.getTopic().getTopicId());
return retValue;
}
public static String getTopicsInContentSpecString(final EntityManager entityManager,
final Integer contentSpecTopicID) throws Exception {
final List<Integer> topics = getTopicsInContentSpec(entityManager, contentSpecTopicID);
if (topics == null || topics.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(topics);
}
/**
* @return A comma separated list of topic ids that have open bugzilla bugs assigned to them
*/
public static String getTopicsWithOpenBugsString(final EntityManager entityManager) {
final List<Integer> topics = getTopicsWithOpenBugs(entityManager);
if (topics.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(topics);
}
/**
* @return A list of topic ids that have bugzilla bugs assigned to them
*/
@SuppressWarnings("unchecked")
public static List<Integer> getTopicsWithBugs(final EntityManager entityManager) {
final List<TopicToBugzillaBug> results = entityManager.createQuery(TopicToBugzillaBug.SELECT_ALL_QUERY).getResultList();
final List<Integer> retValue = new ArrayList<Integer>();
for (final TopicToBugzillaBug map : results)
if (!retValue.contains(map.getTopic().getTopicId())) retValue.add(map.getTopic().getTopicId());
return retValue;
}
@SuppressWarnings("unchecked")
public static List<Integer> getTopicsWithPropertyTag(final EntityManager entityManager, final Integer propertyTagIdInt,
final String propertyTagValue) {
final Query query = entityManager.createQuery(
TopicToPropertyTag.SELECT_ALL_QUERY + " WHERE topicToPropertyTag.propertyTag.propertyTagId = :propTagId AND " +
"topicToPropertyTag.value = :propTagValue");
query.setParameter("propTagId", propertyTagIdInt);
query.setParameter("propTagValue", propertyTagValue);
final List<TopicToPropertyTag> mappings = query.getResultList();
if (mappings.size() == 0) return null;
final List<Integer> retValue = new ArrayList<Integer>();
for (final TopicToPropertyTag mapping : mappings) {
retValue.add(mapping.getTopic().getTopicId());
}
return retValue;
}
public static String getTopicsWithPropertyTagString(final EntityManager entityManager, final Integer propertyTagIdInt,
final String propertyTagValue) {
final List<Integer> topics = getTopicsWithPropertyTag(entityManager, propertyTagIdInt, propertyTagValue);
if (topics.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(topics);
}
@SuppressWarnings("unchecked")
public static List<Integer> getTagsWithPropertyTag(final EntityManager entityManager, final Integer propertyTagIdInt,
final String propertyTagValue) {
final Query query = entityManager.createQuery(
TagToPropertyTag.SELECT_ALL_QUERY + " WHERE tagToPropertyTag.propertyTag.propertyTagId = :propTagId AND tagToPropertyTag" +
".value = :propTagValue");
query.setParameter("propTagId", propertyTagIdInt);
query.setParameter("propTagValue", propertyTagValue);
final List<TagToPropertyTag> mappings = query.getResultList();
if (mappings.size() == 0) return null;
final List<Integer> retValue = new ArrayList<Integer>();
for (final TagToPropertyTag mapping : mappings) {
retValue.add(mapping.getTag().getTagId());
}
return retValue;
}
public static String getTagsWithPropertyTagString(final EntityManager entityManager, final Integer propertyTagIdInt,
final String propertyTagValue) {
final List<Integer> tags = getTagsWithPropertyTag(entityManager, propertyTagIdInt, propertyTagValue);
if (tags.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(tags);
}
/**
* @return A comma separated list of topic ids that have bugzilla bugs assigned to them
*/
public static String getTopicsWithBugsString(final EntityManager entityManager) {
final List<Integer> topics = getTopicsWithBugs(entityManager);
if (topics.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(topics);
}
public static <E> String getEditedEntitiesString(final EntityManager entityManager, final Class<E> type, final String pkColumnName,
final DateTime startDate, final DateTime endDate) {
final List<Integer> ids = getEditedEntities(entityManager, type, pkColumnName, startDate, endDate);
if (ids != null && ids.size() != 0) return CollectionUtilities.toSeperatedString(ids);
return Constants.NULL_TOPIC_ID_STRING;
}
@SuppressWarnings("unchecked")
public static HashMap<Integer, ArrayList<Integer>> populateExclusionTags(final EntityManager entityManager) {
final HashMap<Integer, ArrayList<Integer>> retValue = new HashMap<Integer, ArrayList<Integer>>();
List<Tag> tagList = entityManager.createQuery(Tag.SELECT_ALL_QUERY).getResultList();
for (final Tag tag : tagList)
retValue.put(tag.getTagId(), tag.getExclusionTagIDs());
return retValue;
}
/**
* This function is used to get a list of the currently selected tags. This is used to prepopulate the selected tags when
* creating a new topic
*/
public static String urlTagParameters(final FacesContext context) {
final Map<String, String> paramMap = context.getExternalContext().getRequestParameterMap();
String variables = "";
for (final String key : paramMap.keySet()) {
if (key.startsWith(CommonFilterConstants.MATCH_TAG)) {
if (variables.length() == 0) variables += "?";
else variables += "&";
variables += key + "=" + paramMap.get(key);
}
}
return variables;
}
/**
* A utility function that is used to append url parameters to a collection of url parameters
*
* @param params The existing url parameter string
* @param name The parameter name
* @param value The parameter value
* @return The url parameters that were passed in via params, with the new parameter appended to it
*/
public static String addParameter(final String params, final String name, final String value) {
// Just use an empty string as the default value
return addParameter(params, name, value, "");
}
/**
* A utility function that is used to append url parameters to a collection of url parameters
*
* @param params The existing url parameter string
* @param name The parameter name
* @param value The parameter value
* @param defaultValue Used in place of value if it is null
* @return The url parameters that were passed in via params, with the new parameter appended to it
*/
public static String addParameter(final String params, final String name, final String value, final String defaultValue) {
String newParams = params;
if (newParams.length() == 0) newParams += "?";
else newParams += "&";
newParams += name + "=" + (value == null ? defaultValue : value);
return newParams;
}
public static String cleanStringForJavaScriptVariableName(final String input) {
return input.replaceAll("[^a-zA-Z]", "");
}
public static String getIncomingRelationshipsTo(final EntityManager entityManager, final Integer topicId) {
final List<Integer> ids = getIncomingRelatedTopicIDs(entityManager, topicId);
if (ids != null && ids.size() != 0) return CollectionUtilities.toSeperatedString(ids);
return Constants.NULL_TOPIC_ID_STRING;
}
public static String getOutgoingRelationshipsFrom(final EntityManager entityManager, final Integer topicId) {
final List<Integer> ids = getOutgoingRelatedTopicIDs(entityManager, topicId);
if (ids != null && ids.size() != 0) return CollectionUtilities.toSeperatedString(ids);
return Constants.NULL_TOPIC_ID_STRING;
}
public static List<Topic> getTopicsFromFilter(final EntityManager entityManager, final Filter filter) {
// build the query that will be used to get the topics
final CriteriaQuery<Topic> query = FilterUtilities.buildQuery(filter, new TopicFilterQueryBuilder(entityManager));
// get the base topic list
final List<Topic> topicList = entityManager.createQuery(query).getResultList();
return topicList;
}
@SuppressWarnings("unchecked")
public static List<Integer> getLatestTranslatedTopics(final EntityManager entityManager) {
String query = TranslatedTopicData.SELECT_ALL_QUERY;
query += " where translatedTopicData.translatedTopic.topicRevision = (Select MAX(B.translatedTopic.topicRevision) FROM " +
"TranslatedTopicData B WHERE translatedTopicData.translatedTopic.topicId = B.translatedTopic.topicId AND B" +
".locale.localeId = translatedTopicData.locale.localeId GROUP BY B.translatedTopic.topicId)";
final List<TranslatedTopicData> results = entityManager.createQuery(query).getResultList();
final List<Integer> retValue = new ArrayList<Integer>();
for (final TranslatedTopicData topic : results)
if (!retValue.contains(topic.getTranslatedTopicDataId())) retValue.add(topic.getTranslatedTopicDataId());
return retValue;
}
@SuppressWarnings("unchecked")
public static List<Integer> getLatestCompletedTranslatedTopics(final EntityManager entityManager) {
String query = TranslatedTopicData.SELECT_ALL_QUERY;
query += " where translatedTopicData.translatedTopic.topicRevision = (Select MAX(B.translatedTopic.topicRevision) FROM " +
"TranslatedTopicData B WHERE translatedTopicData.translatedTopic.topicId = B.translatedTopic.topicId AND B" +
".locale.localeId = translatedTopicData.locale.localeId AND B.translationPercentage >= 100 GROUP BY B.translatedTopic" +
".topicId)";
final List<TranslatedTopicData> results = entityManager.createQuery(query).getResultList();
final List<Integer> retValue = new ArrayList<Integer>();
for (final TranslatedTopicData topic : results)
if (!retValue.contains(topic.getTranslatedTopicDataId())) retValue.add(topic.getTranslatedTopicDataId());
return retValue;
}
public static String getLatestTranslatedTopicsString(final EntityManager entityManager) {
final List<Integer> topics = getLatestTranslatedTopics(entityManager);
if (topics.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(topics);
}
public static String getLatestCompletedTranslatedTopicsString(final EntityManager entityManager) {
final List<Integer> topics = getLatestCompletedTranslatedTopics(entityManager);
if (topics.size() == 0) return Constants.NULL_TOPIC_ID_STRING;
return CollectionUtilities.toSeperatedString(topics);
}
@SuppressWarnings("unchecked")
public static List<Integer> getImagesWithFileName(final EntityManager entityManager, final String filename) {
final Query query = entityManager.createQuery(
LanguageImage.SELECT_ALL_QUERY + (" WHERE LOWER(languageImage.originalFileName) LIKE LOWER(:filename)"));
query.setParameter("filename", "%" + filename + "%");
final List<LanguageImage> mappings = query.getResultList();
if (mappings.size() == 0) return null;
final List<Integer> retValue = new ArrayList<Integer>();
for (final LanguageImage mapping : mappings) {
retValue.add(mapping.getImageFile().getId());
}
return retValue;
}
public static Long getNumberOfRunningContentSpecProcessesForType(final EntityManager entityManager,
final Integer contentSpecId, final ProcessType type) {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
final Root<ContentSpecToProcess> root = query.from(ContentSpecToProcess.class);
query.select(criteriaBuilder.count(root));
final Predicate contentSpecIdMatches = criteriaBuilder.equal(root.get("contentSpec").get("contentSpecId"), contentSpecId);
final Predicate processStatus = root.get("process").get("status").in(EXECUTING_STATUSES);
final Predicate processType = criteriaBuilder.equal(root.get("process").get("type").as(ProcessType.class),
type);
query.where(criteriaBuilder.and(contentSpecIdMatches, processStatus, processType));
return entityManager.createQuery(query).getSingleResult();
}
public static List<ContentSpecToProcess> getRunningContentSpecProcessesForType(final EntityManager entityManager,
final Integer contentSpecId, final ProcessType type) {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<ContentSpecToProcess> query = criteriaBuilder.createQuery(ContentSpecToProcess.class);
final Root<ContentSpecToProcess> root = query.from(ContentSpecToProcess.class);
final Predicate contentSpecIdMatches = criteriaBuilder.equal(root.get("contentSpec").get("contentSpecId"), contentSpecId);
final Predicate processStatus = root.get("process").get("status").in(EXECUTING_STATUSES);
final Predicate processType = criteriaBuilder.equal(root.get("process").get("type").as(ProcessType.class),
type);
query.where(criteriaBuilder.and(contentSpecIdMatches, processStatus, processType));
return entityManager.createQuery(query).getResultList();
}
}