Java tutorial
/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program 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 General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.enterprise.server.util; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Query; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.annotations.IndexColumn; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.criteria.AlertCriteria; import org.rhq.core.domain.criteria.Criteria; import org.rhq.core.domain.criteria.ResourceCriteria; import org.rhq.core.domain.criteria.ResourceGroupCriteria; import org.rhq.core.domain.criteria.ResourceOperationHistoryCriteria; import org.rhq.core.domain.criteria.SubjectCriteria; import org.rhq.core.domain.operation.OperationRequestStatus; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.search.SearchSubsystem; import org.rhq.core.domain.tagging.Tag; import org.rhq.core.domain.util.OrderingField; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageOrdering; import org.rhq.core.server.PersistenceUtility; import org.rhq.core.util.exception.ThrowableUtil; import org.rhq.enterprise.server.search.SearchExpressionException; import org.rhq.enterprise.server.search.execution.SearchTranslationManager; /** * A query generator used to generate queries with specific filtering, prefetching, or sorting requirements. * * @author Joseph Marques */ public final class CriteriaQueryGenerator { private static final Log LOG = LogFactory.getLog(CriteriaQueryGenerator.class); public enum AuthorizationTokenType { RESOURCE, // specifies the resource alias to join on for standard res-group-role-subject authorization checking GROUP; // specifies the group alias to join on for standard group-role-subject authorization checking } private Criteria criteria; private String searchExpressionWhereClause; private Subject subject; private String authorizationPermsFragment; private String authorizationCustomConditionFragment; private int authorizationSubjectId; private String alias; private String className; private String projection; private String groupByClause; private String havingClause; private static String NL = System.getProperty("line.separator"); private static List<String> EXPRESSION_START_KEYWORDS; private List<Field> persistentBagFields = new ArrayList<Field>(); private List<Field> joinFetchFields = new ArrayList<Field>(); static { EXPRESSION_START_KEYWORDS = new ArrayList<String>(2); EXPRESSION_START_KEYWORDS.add("NOT"); EXPRESSION_START_KEYWORDS.add("EXISTS"); } public CriteriaQueryGenerator(Criteria criteria) { this(LookupUtil.getSubjectManager().getOverlord(), criteria); } public CriteriaQueryGenerator(Subject subject, Criteria criteria) { this.subject = subject; this.criteria = criteria; this.className = criteria.getPersistentClass().getSimpleName(); this.alias = this.criteria.getAlias(); initializeJPQLFragmentFromSearchExpression(); } public void setAuthorizationCustomConditionFragment(String fragment) { this.authorizationCustomConditionFragment = fragment; } public void setAuthorizationResourceFragment(AuthorizationTokenType type, int subjectId) { String defaultFragment = null; if (type == AuthorizationTokenType.RESOURCE) { defaultFragment = "resource"; } else if (type == AuthorizationTokenType.GROUP) { defaultFragment = "group"; } setAuthorizationResourceFragment(type, defaultFragment, subjectId); } private String fixFilterOverride(String expression, String fieldName) { boolean fuzzyMatch = expression.toLowerCase().contains(" like ") && !expression.toLowerCase().contains("select"); // Don't fuzzy match subselects boolean wantCaseInsensitiveMatch = !criteria.isCaseSensitive() && fuzzyMatch; while (expression.indexOf('?') != -1) { String replacement = ":" + fieldName; expression = expression.replaceFirst("\\?", replacement); } // if the override expression does not follow the usual format of ( field operator expression ) // then don't prepend the alias or deal with other special handling. The override must be left // explicit. if (!expressionStartsWithKeyword(expression)) { if (wantCaseInsensitiveMatch) { int indexOfFirstSpace = expression.indexOf(" "); String filterToken = expression.substring(0, indexOfFirstSpace); expression = "LOWER( " + alias + "." + filterToken + " ) " + expression.substring(indexOfFirstSpace); } else { expression = alias + "." + expression; } } if (fuzzyMatch) { expression += QueryUtility.getEscapeClause(); } return expression; } private boolean expressionStartsWithKeyword(String expression) { expression = expression.trim(); int i = expression.trim().indexOf(" "); String startToken = expression.substring(0, i); return EXPRESSION_START_KEYWORDS.contains(startToken.toUpperCase()); } public void setAuthorizationResourceFragment(AuthorizationTokenType type, String fragment, int subjectId) { this.authorizationSubjectId = subjectId; if (type == AuthorizationTokenType.RESOURCE) { setAuthorizationCustomConditionFragment( getEnhancedResourceAuthorizationWhereFragment(fragment, subjectId)); } else if (type == AuthorizationTokenType.GROUP) { // support for: 1) role-based for groups, 2) role-based for containing cluster groups, 3) private groups setAuthorizationCustomConditionFragment( getEnhancedGroupAuthorizationWhereFragment(fragment, subjectId)); } else { throw new IllegalArgumentException(this.getClass().getSimpleName() + " does not yet support generating queries for '" + type + "' token types"); } // If the query results are narrowed by requiredParams generate the fragment now. It's done // here for two reasons. First, it seems to make sense to apply this only when an authFragment is // being used. Second, because ond day the query may be less brute force and may modify or // leverage the joinFragment above. But, after extensive trying a more elegant // query could not be constructed due to Hibernate limitations. So, for now, here it is... List<Permission> requiredPerms = this.criteria.getRequiredPermissions(); if (!(null == requiredPerms || requiredPerms.isEmpty())) { this.authorizationPermsFragment = "" // + "( SELECT COUNT(DISTINCT p)" + NL // + " FROM Subject innerSubject" + NL // + " JOIN innerSubject.roles r" + NL // + " JOIN r.permissions p" + NL // + " WHERE innerSubject.id = " + this.authorizationSubjectId + NL // + " AND p IN ( :requiredPerms ) ) = :requiredPermsSize" + NL; } } private String getEnhancedResourceAuthorizationWhereFragment(String fragment, int subjectId) { String customAuthzFragment = "" // + "( %aliasWithFragment%.id IN ( SELECT %innerAlias%.id " + NL // + " FROM %alias% innerAlias " + NL // + " JOIN %innerAlias%.implicitGroups g JOIN g.roles r JOIN r.subjects s " + NL // + " WHERE s.id = %subjectId% ) )" + NL; // String aliasReplacement = criteria.getAlias() + (fragment != null ? "." + fragment : ""); String innerAliasReplacement = "innerAlias" + (fragment != null ? "." + fragment : ""); customAuthzFragment = customAuthzFragment.replace("%alias%", criteria.getAlias()); customAuthzFragment = customAuthzFragment.replace("%aliasWithFragment%", aliasReplacement); customAuthzFragment = customAuthzFragment.replace("%innerAlias%", innerAliasReplacement); customAuthzFragment = customAuthzFragment.replace("%subjectId%", String.valueOf(subjectId)); return customAuthzFragment; } private String getEnhancedGroupAuthorizationWhereFragment(String fragment, int subjectId) { String customAuthzFragment = "" // + "( %aliasWithFragment%.id IN ( SELECT %innerAlias%.id " + NL // + " FROM %alias% innerAlias " + NL // + " JOIN %innerAlias%.roles r JOIN r.subjects s " + NL // + " WHERE s.id = %subjectId% )" + NL // + " OR" + NL // + " %aliasWithFragment%.id IN ( SELECT %innerAlias%.id " + NL // + " FROM %alias% innerAlias " + NL // + " JOIN %innerAlias%.clusterResourceGroup crg JOIN crg.roles r JOIN r.subjects s " + NL // + " WHERE crg.recursive = true AND s.id = %subjectId% )" + NL // + " OR" + NL // + " %aliasWithFragment%.id IN ( SELECT %innerAlias%.id" + NL // + " FROM %alias% innerAlias " + NL // + " JOIN %innerAlias%.subject s" + NL // + " WHERE s.id = %subjectId% ) ) " + NL; String aliasReplacement = criteria.getAlias() + (fragment != null ? "." + fragment : ""); String innerAliasReplacement = "innerAlias" + (fragment != null ? "." + fragment : ""); customAuthzFragment = customAuthzFragment.replace("%alias%", criteria.getAlias()); customAuthzFragment = customAuthzFragment.replace("%aliasWithFragment%", aliasReplacement); customAuthzFragment = customAuthzFragment.replace("%innerAlias%", innerAliasReplacement); customAuthzFragment = customAuthzFragment.replace("%subjectId%", String.valueOf(subjectId)); return customAuthzFragment; } public String getParameterReplacedQuery(boolean countQuery) { String query = getQueryString(countQuery); for (Map.Entry<String, Object> critField : getFilterFields(criteria).entrySet()) { Object value = critField.getValue(); if (value instanceof Tag) { Tag tag = (Tag) value; query = query.replace(":tagNamespace", tag.getNamespace()); query = query.replace(":tagSemantic", tag.getSemantic()); query = query.replace(":tagName", tag.getName()); } else { value = getParameterReplacedValue(value); if (LOG.isDebugEnabled()) { LOG.debug("Bind: (" + critField.getKey() + ", " + value + ")"); } query = query.replace(":" + critField.getKey(), String.valueOf(value)); } } if (null != this.authorizationPermsFragment) { List<Permission> requiredPerms = this.criteria.getRequiredPermissions(); String perms = requiredPerms.toString(); // [data1, data, data3] query = query.replace(":requiredPerms", perms.subSequence(1, perms.length() - 1)); // remove first/last characters query = query.replace(":requiredPermsSize", String.valueOf(requiredPerms.size())); } return query; } private String getParameterReplacedValue(Object value) { String returnValue; if (value instanceof String) { returnValue = "'" + prepareStringBindValue((String) value) + "'"; } else if (value instanceof Enum<?>) { // note: this strategy won't work for entities with multiple enums that are persisted differently EnumType type = getPersistenceEnumType(value.getClass()); if (type == EnumType.STRING) { returnValue = "'" + String.valueOf(value) + "'"; } else { returnValue = String.valueOf(value); } } else if (value instanceof List<?>) { List<?> valueList = (List<?>) value; StringBuilder results = new StringBuilder(); boolean first = true; for (Object nextValue : valueList) { if (first) { first = false; } else { results.append(","); } results.append(getParameterReplacedValue(nextValue)); } returnValue = results.toString(); } else { returnValue = String.valueOf(value); } return returnValue; } // calculates @Enumerated(EnumType.STRING) or @Enumerated(EnumType.ORDINAL) private EnumType getPersistenceEnumType(Class<?> enumFieldType) { for (Field nextField : getClass().getFields()) { nextField.setAccessible(true); if (nextField.getType().equals(enumFieldType)) { Enumerated enumeratedAnnotation = nextField.getAnnotation(Enumerated.class); if (enumeratedAnnotation != null) { return enumeratedAnnotation.value(); } } } return EnumType.STRING; // catch-all } public String getQueryString(boolean countQuery) { StringBuilder results = new StringBuilder(); results.append("SELECT "); if (countQuery) { if (groupByClause == null) { // non-grouped method // use count(*) instead of count(alias) due to https://bugzilla.redhat.com/show_bug.cgi?id=699842 results.append("COUNT(*)").append(NL); } else { // gets the count of the number of aggregate/grouped rows // NOTE: this only works when the groupBy is a single element, as opposed to a list of elements results.append("COUNT(DISTINCT ").append(groupByClause).append(")").append(NL); } } else { if (projection == null) { results.append(alias).append(NL); } else { results.append(projection).append(NL); } } results.append("FROM ").append(className).append(' ').append(alias).append(NL); if (countQuery == false) { /* * don't fetch in the count query to avoid: "query specified join fetching, * but the owner of the fetched association was not present in the select list" */ for (String fetchField : getFetchFields(criteria)) { if (isPersistentBag(fetchField)) { addPersistentBag(fetchField); } else { if (this.projection == null) { /* * if not altering the projection, join fetching can be using * to retrieve the associated instance in the same SELECT */ results.append("LEFT JOIN FETCH ").append(alias).append('.').append(fetchField).append(NL); } else { /* * if the projection is altered (perhaps converting it into a constructor query), then all * fields specified in the fetch must be in the explicit return list. this is not possible * today with constructor queries, so any altered projection will implicitly disable fetching. * instead, we'll record which fields need to be explicitly fetched after the primary query * returns the bulk of the data, and use a similar methodology at the SLSB layer to eagerly * load those before returning the PageList back to the caller. */ addJoinFetch(fetchField); } } } } // figure out the 'LEFT JOIN's needed for 'ORDER BY' tokens PageControl pc = getPageControl(criteria); List<String> orderingFieldRequiredJoins = new ArrayList<String>(); List<String> orderingFieldTokens = new ArrayList<String>(); for (OrderingField orderingField : pc.getOrderingFields()) { PageOrdering ordering = orderingField.getOrdering(); String fieldName = orderingField.getField(); String override = criteria.getJPQLSortOverride(fieldName); String suffix = (override == null) ? fieldName : override; /* * do not prefix the alias when: * * 1) if the suffix is numerical, which allows us to sort by column ordinal * 2) if the user wants full control and has explicitly chosen to disable alias prepending */ boolean doNotPrefixAlias = isNumber(suffix) || criteria.hasCustomizedSorting(); String sortFragment = doNotPrefixAlias ? suffix : (alias + "." + suffix); if (criteria.hasCustomizedSorting()) { // customized sorting does not get LEFT JOIN expressions added orderingFieldTokens.add(sortFragment + " " + ordering); continue; } int lastDelimiterIndex = sortFragment.lastIndexOf('.'); if (lastDelimiterIndex == -1) { // does not require joins, just add the ordering field token directly orderingFieldTokens.add(sortFragment + " " + ordering); continue; } int firstDelimiterIndex = sortFragment.indexOf('.'); if (firstDelimiterIndex == lastDelimiterIndex) { // only one dot implies its a property/field directly off of the primary alias // thus, also does not require joins, just add the ordering field token directly orderingFieldTokens.add(sortFragment + " " + ordering); continue; } String expressionRoot = sortFragment.substring(0, lastDelimiterIndex); String expressionLeaf = sortFragment.substring(lastDelimiterIndex + 1); int expressionRootIndex = orderingFieldRequiredJoins.indexOf(expressionRoot); String joinAlias; if (expressionRootIndex == -1) { // new join joinAlias = "orderingField" + orderingFieldRequiredJoins.size(); orderingFieldRequiredJoins.add(expressionRoot); results.append("LEFT JOIN ").append(expressionRoot).append(" ").append(joinAlias).append(NL); } else { joinAlias = "orderingField" + expressionRootIndex; } orderingFieldTokens.add(joinAlias + "." + expressionLeaf + " " + ordering); } Map<String, Object> filterFields = getFilterFields(criteria); if (filterFields.size() > 0 || authorizationPermsFragment != null || authorizationCustomConditionFragment != null || searchExpressionWhereClause != null) { results.append("WHERE "); } String conjunctiveFragment = criteria.isFiltersOptional() ? "OR " : "AND "; boolean wantCaseInsensitiveMatch = !criteria.isCaseSensitive(); // criteria StringBuilder conjunctiveResults = new StringBuilder(); boolean firstCrit = true; for (Map.Entry<String, Object> filterField : filterFields.entrySet()) { Object filterFieldValue = filterField.getValue(); // if this filter field is non-binding (that is, the query has no parameter whose value is to be bound for the field) // and that filter field is turned off, do nothing and continue to the next filter. // this in effect does not filter on this field at all. if (Criteria.NonBindingOverrideFilter.OFF.equals(filterFieldValue)) { continue; } if (firstCrit) { firstCrit = false; } else { conjunctiveResults.append(NL).append(conjunctiveFragment); } String fieldName = filterField.getKey(); String override = criteria.getJPQLFilterOverride(fieldName); String fragment; if (override != null) { fragment = fixFilterOverride(override, fieldName); } else { String operator = "="; if (filterFieldValue instanceof String) { operator = "like"; if (wantCaseInsensitiveMatch) { fragment = "LOWER( " + alias + "." + fieldName + " ) " + operator + " :" + fieldName; } else { fragment = alias + "." + fieldName + " " + operator + " :" + fieldName; } fragment += QueryUtility.getEscapeClause(); } else { fragment = alias + "." + fieldName + " " + operator + " :" + fieldName; } } conjunctiveResults.append(fragment).append(' '); } if (conjunctiveResults.length() > 0) { results.append("( ").append(conjunctiveResults).append(")"); } // authorization if (authorizationPermsFragment != null) { if (firstCrit) { firstCrit = false; } else { // always want AND for security, regardless of conjunctiveFragment results.append(NL).append(" AND "); } results.append(this.authorizationPermsFragment).append(" "); } if (authorizationCustomConditionFragment != null) { if (firstCrit) { firstCrit = false; } else { // always want AND for security, regardless of conjunctiveFragment results.append(NL).append(" AND "); } results.append(this.authorizationCustomConditionFragment); } if (searchExpressionWhereClause != null) { if (firstCrit) { firstCrit = false; } else { // always want to additionally filter by translated from the RHQL search expression results.append(NL).append(" AND "); } results.append(searchExpressionWhereClause); } if (countQuery == false) { // group by clause if (groupByClause != null) { results.append(NL).append("GROUP BY ").append(groupByClause); } // having clause if (havingClause != null) { results.append(NL).append("HAVING ").append(havingClause); } // ordering clause boolean first = true; for (String next : orderingFieldTokens) { if (first) { results.append(NL).append("ORDER BY "); first = false; } else { results.append(", "); } results.append(next); } } results.append(NL); LOG.debug(results); return results.toString(); } private boolean isNumber(String input) { if (input == null) { return false; } for (char next : input.toCharArray()) { if (Character.isDigit(next) == false) { return false; } } return true; } public List<String> getFetchFields(Criteria criteria) { List<String> results = new ArrayList<String>(); for (Field fetchField : getFields(criteria, Criteria.Type.FETCH)) { Object fetchFieldValue; try { fetchField.setAccessible(true); fetchFieldValue = fetchField.get(criteria); } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } if (fetchFieldValue != null) { boolean shouldFetch = ((Boolean) fetchFieldValue).booleanValue(); if (shouldFetch) { results.add(getCleansedFieldName(fetchField, 5)); } } } // for (String entry : results) { // LOG.info("Fetch: (" + entry + ")"); // } return results; } private static List<Field> getFields(Criteria criteria, Criteria.Type fieldType) { String prefix = fieldType.name().toLowerCase(); List<Field> results = new ArrayList<Field>(); Class<?> currentLevelClass = criteria.getClass(); while (currentLevelClass.equals(Criteria.class) == false) { for (Field field : currentLevelClass.getDeclaredFields()) { field.setAccessible(true); if (field.getName().startsWith(prefix)) { results.add(field); } } currentLevelClass = currentLevelClass.getSuperclass(); } return results; } public static String getCleansedFieldName(Field field, int leadingCharsToStrip) { String fieldNameFragment = field.getName().substring(leadingCharsToStrip); String fieldName = Character.toLowerCase(fieldNameFragment.charAt(0)) + fieldNameFragment.substring(1); return fieldName; } public Map<String, Object> getFilterFields(Criteria criteria) { Map<String, Object> results = new HashMap<String, Object>(); for (Field filterField : getFields(criteria, Criteria.Type.FILTER)) { Object filterFieldValue; try { filterFieldValue = filterField.get(criteria); } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } if (filterFieldValue != null) { results.put(getCleansedFieldName(filterField, 6), filterFieldValue); } } // for (Map.Entry<String, Object> entries : results.entrySet()) { // LOG.info("Filter: (" + entries.getKey() + ", " + entries.getValue() + ")"); // } return results; } private void initializeJPQLFragmentFromSearchExpression() { String searchExpression = criteria.getSearchExpression(); if (searchExpression == null) { return; } try { Class<?> entityClass = criteria.getPersistentClass(); SearchTranslationManager searchManager = new SearchTranslationManager(criteria.getAlias(), subject, SearchSubsystem.get(entityClass)); searchManager.setExpression(searchExpression); // translate first, if there was an error we won't add the dangling 'AND' to the where clause String translatedJPQL = searchManager.getJPQLWhereFragment(); LOG.debug("Translated JPQL Fragment was: " + translatedJPQL); if (translatedJPQL != null) { searchExpressionWhereClause = translatedJPQL; } } catch (SearchExpressionException see) { throw see; // bubble up to the top } catch (RuntimeException re) { LOG.error("Could not get JPQL translation for '" + searchExpression + "': " + ThrowableUtil.getAllMessages(re, true)); throw re; // don't wrap exceptions that are already RuntimeExceptions in another RuntimeException } catch (Exception e) { LOG.error("Could not get JPQL translation for '" + searchExpression + "': " + ThrowableUtil.getAllMessages(e, true)); throw new RuntimeException(e); } } private boolean isPersistentBag(String fieldName) { try { Class<?> persistentClass = criteria.getPersistentClass(); Field field = persistentClass.getDeclaredField(fieldName); return isAList(field) && !field.isAnnotationPresent(IndexColumn.class); } catch (NoSuchFieldException e) { return false; } } private boolean isAList(Field field) { Class<?> fieldType = field.getType(); if (List.class.isAssignableFrom(fieldType)) { return true; } for (Class<?> declaredInterface : fieldType.getInterfaces()) { if (List.class.isAssignableFrom(declaredInterface)) { return true; } } return false; } private void addPersistentBag(String fieldName) { try { Field field = criteria.getPersistentClass().getDeclaredField(fieldName); persistentBagFields.add(field); } catch (NoSuchFieldException e) { LOG.warn("Failed to add persistent bag collection.", e); } } private void addJoinFetch(String fieldName) { try { Field field = criteria.getPersistentClass().getDeclaredField(fieldName); joinFetchFields.add(field); } catch (NoSuchFieldException e) { LOG.warn("Failed to add join fetch field.", e); } } /** * <strong>Note:</strong> This method should only be called after {@link #getQueryString(boolean)}} because it is * that method where the persistentBagFields property is initialized. * * @return Returns a list of fields from the persistent class to which the criteria class corresponds. The fields in * the list are themselves instances of List and have "bag" semantics. */ public List<Field> getPersistentBagFields() { return persistentBagFields; } public List<Field> getJoinFetchFields() { return joinFetchFields; } /** * If you want to return something other than the list of entities represented by the passed Criteria object, * you can alter the projection here to return a customized subset or superset of data. The projection will * only affect the ResultSet for the data query, not the count query. * <p/> * If you are projecting a composite object that does not directly extend the entity your Criteria object * represents, then you will need to manually initialize the persistent bags using the methods exposed on * {@link CriteriaQueryRunner} */ public void alterProjection(String projection) { this.projection = projection; } public boolean isProjectionAltered() { return this.projection != null; } /** * The groupBy clause can be set if and only if the projection is altered. The passed argument should not be * prefixed with 'group by'; that part of the query will be auto-generated if the argument is non-null. The * new projection must follow standard rules as they apply to statements with groupBy clauses. */ public void setGroupByClause(String groupByClause) { if (groupByClause != null && projection == null) { throw new IllegalArgumentException("Must alter projection before calling setGroupByClause"); } this.groupByClause = groupByClause; } /** * The having clause can be set if and only if the groupBy clause is set. The passed argument should not be * prefixed with 'having'; that part of the query will be auto-generated if the argument is non-null. The * having clause must follow standard rules as they apply to statements with groupBy clauses. */ public void setHavingClause(String havingClause) { if (havingClause != null && groupByClause == null) { throw new IllegalArgumentException("Must add some groupBy clause before calling setHavingClause"); } this.havingClause = havingClause; } public Query getQuery(EntityManager em) { String queryString = getQueryString(false); Query query = em.createQuery(queryString); setBindValues(query); PersistenceUtility.setDataPage(query, getPageControl(criteria)); return query; } public Query getCountQuery(EntityManager em) { String countQueryString = getQueryString(true); Query query = em.createQuery(countQueryString); setBindValues(query); return query; } private void setBindValues(Query query) { for (Map.Entry<String, Object> critField : getFilterFields(criteria).entrySet()) { Object value = critField.getValue(); if (value instanceof Tag) { Tag tag = (Tag) value; query.setParameter("tagNamespace", tag.getNamespace()); query.setParameter("tagSemantic", tag.getSemantic()); query.setParameter("tagName", tag.getName()); } else if (value instanceof Criteria.NonBindingOverrideFilter) { // skip this one - do nothing since there is no parameter binding for this value } else { if (value instanceof String) { value = prepareStringBindValue((String) value); } if (LOG.isDebugEnabled()) { LOG.debug("Bind: (" + critField.getKey() + ", " + value + ")"); } query.setParameter(critField.getKey(), value); } } if (null != this.authorizationPermsFragment) { List<Permission> requiredPerms = this.criteria.getRequiredPermissions(); query.setParameter("requiredPerms", requiredPerms); query.setParameter("requiredPermsSize", (long) requiredPerms.size()); } } private String prepareStringBindValue(String value) { if (!criteria.isStrict()) { value = "%" + QueryUtility.escapeSearchParameter(value) + "%"; } if (!criteria.isCaseSensitive()) { value = value.toLowerCase(); } return value; } public static void main(String[] args) { //testSubjectCriteria(); //testAlertCriteria(); //testInheritanceCriteria(); //testResourceCriteria(); testResourceGroupCriteria(); } public static void testSubjectCriteria() { SubjectCriteria subjectCriteria = new SubjectCriteria(); subjectCriteria.addFilterFirstName("joe"); subjectCriteria.addFilterFactive(true); subjectCriteria.fetchRoles(true); subjectCriteria.addSortName(PageOrdering.ASC); Subject overlord = LookupUtil.getSubjectManager().getOverlord(); CriteriaQueryGenerator subjectGenerator = new CriteriaQueryGenerator(overlord, subjectCriteria); System.out.println(subjectGenerator.getQueryString(false)); System.out.println(subjectGenerator.getQueryString(true)); } public static void testAlertCriteria() { AlertCriteria alertCriteria = new AlertCriteria(); alertCriteria.addFilterName("joe"); alertCriteria.addFilterDescription("query generation is cool"); alertCriteria.addFilterStartTime(42L); alertCriteria.addFilterEndTime(100L); alertCriteria.addFilterResourceIds(1, 2, 3); alertCriteria.fetchAlertDefinition(true); alertCriteria.addSortPriority(PageOrdering.DESC); alertCriteria.addSortName(PageOrdering.ASC); alertCriteria.setPaging(0, 100); alertCriteria.setFiltersOptional(true); //alertCriteria.setCaseSensitive(false); Subject overlord = LookupUtil.getSubjectManager().getOverlord(); CriteriaQueryGenerator generator = new CriteriaQueryGenerator(overlord, alertCriteria); System.out.println(generator.getQueryString(false)); System.out.println(generator.getQueryString(true)); generator.setAuthorizationResourceFragment(AuthorizationTokenType.RESOURCE, "definition.resource", 1); System.out.println(generator.getQueryString(false)); System.out.println(generator.getQueryString(true)); } public static void testInheritanceCriteria() { ResourceOperationHistoryCriteria historyCriteria = new ResourceOperationHistoryCriteria(); historyCriteria.addFilterResourceIds(1); historyCriteria.addFilterStatus(OperationRequestStatus.FAILURE); Subject overlord = LookupUtil.getSubjectManager().getOverlord(); CriteriaQueryGenerator generator = new CriteriaQueryGenerator(overlord, historyCriteria); System.out.println(generator.getQueryString(false)); System.out.println(generator.getQueryString(true)); } public static void testResourceGroupCriteria() { ResourceGroupCriteria groupCriteria = new ResourceGroupCriteria(); groupCriteria.addSortName(PageOrdering.DESC); groupCriteria.addSortResourceTypeName(PageOrdering.ASC); groupCriteria.addSortPluginName(PageOrdering.DESC); CriteriaQueryGenerator generator = new CriteriaQueryGenerator(new Subject(), groupCriteria); System.out.println(generator.getQueryString(false)); System.out.println(generator.getQueryString(true)); PageControl customPC = new PageControl(); customPC.addDefaultOrderingField("0", PageOrdering.DESC); customPC.addDefaultOrderingField("name", PageOrdering.DESC); customPC.addDefaultOrderingField("resourceType.name", PageOrdering.ASC); groupCriteria.setPageControl(customPC); System.out.println(generator.getQueryString(false)); System.out.println(generator.getQueryString(true)); } public static void testResourceCriteria() { ResourceCriteria resourceCriteria = new ResourceCriteria(); resourceCriteria.addFilterResourceCategories(ResourceCategory.SERVER); resourceCriteria.addFilterName("marques"); resourceCriteria.fetchAgent(true); resourceCriteria.addSortResourceTypeName(PageOrdering.ASC); resourceCriteria.setCaseSensitive(true); resourceCriteria.setFiltersOptional(true); Subject overlord = LookupUtil.getSubjectManager().getOverlord(); CriteriaQueryGenerator generator = new CriteriaQueryGenerator(overlord, resourceCriteria); System.out.println(generator.getQueryString(false)); System.out.println(generator.getQueryString(true)); } public static PageControl getPageControl(Criteria criteria) { PageControl pc; if (criteria.getPageControlOverrides() != null) { pc = criteria.getPageControlOverrides(); } else { if (criteria.getPageNumber() == null || criteria.getPageSize() == null) { pc = PageControl.getUnlimitedInstance(); } else { pc = new PageControl(criteria.getPageNumber(), criteria.getPageSize()); } for (String fieldName : criteria.getOrderingFieldNames()) { for (Field sortField : getFields(criteria, Criteria.Type.SORT)) { if (sortField.getName().equals(fieldName) == false) { continue; } Object sortFieldValue; try { sortFieldValue = sortField.get(criteria); } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } if (sortFieldValue != null) { PageOrdering pageOrdering = (PageOrdering) sortFieldValue; pc.addDefaultOrderingField(getCleansedFieldName(sortField, 4), pageOrdering); } } } } return pc; } }