Java tutorial
/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.gutterball.curator; import org.candlepin.common.config.PropertyConverter; import org.candlepin.common.paging.Page; import org.candlepin.common.paging.PageRequest; import org.candlepin.gutterball.model.snapshot.Compliance; import org.candlepin.gutterball.util.AutoEvictingColumnarResultsIterator; import com.google.inject.Inject; import com.google.inject.persist.Transactional; import org.hibernate.CacheMode; import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Property; import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.Subqueries; import org.hibernate.sql.JoinType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.persistence.CacheRetrieveMode; import javax.persistence.CacheStoreMode; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Root; /** * The curator responsible for managing {@link Compliance} objects. */ public class ComplianceSnapshotCurator extends BaseCurator<Compliance> { private static Logger log = LoggerFactory.getLogger(ComplianceSnapshotCurator.class); @Inject public ComplianceSnapshotCurator() { super(Compliance.class); } /** * Fetches the row count for the specified criteria. The criteria's projections and result * transformer will be reset in the process. * * @param criteria * The Criteria for which to retrieve the row count. * * @return * The number of rows returned from the database for the given Criteria-based query. */ protected int getRowCount(Criteria criteria) { criteria.setProjection(Projections.rowCount()); int count = ((Number) criteria.uniqueResult()).intValue(); criteria.setProjection(null); criteria.setResultTransformer(Criteria.ROOT_ENTITY); return count; } /** * Retrieves an iterator over the compliance snapshots on the target date. * * @param targetDate * The date for which to retrieve compliance snapshots. If null, the current date will be used * instead. * * @param consumerUuids * A list of consumer UUIDs to use to filter the results. If provided, only compliances for * consumers in the list will be retrieved. * * @param ownerFilters * A list of owners to use to filter the results. If provided, only compliances for consumers * belonging to the specified owners (orgs) will be retrieved. * * @param statusFilters * A list of statuses to use to filter the results. If provided, only compliances with a status * matching the list will be retrieved. * * @param productNameFilters * A list of product names to use to filter compliances. If provided, only compliances for * consumers having installed the specified products will be retrieved. * * @param subscriptionSkuFilters * A list of subscription skus to use to filter compliances. If provided, only compliances for * the specified subscription skus will be retrieved. * * @param subscriptionNameFilters * A list of subscription names to use to filter compliances. If provided, only compliances for * the specified subscription names will be retrieved. * * @param attributeFilters * A map of entitlement attributes to use to filter compliances. If provided, only compliances * for entitlements having the specified values for the given attributes will be retrieved. * * @return * An iterator over the compliance snapshots for the target date. */ public Iterator<Compliance> getSnapshotIterator(Date targetDate, List<String> consumerUuids, List<String> ownerFilters, List<String> statusFilters, List<String> productNameFilters, List<String> subscriptionSkuFilters, List<String> subscriptionNameFilters, Map<String, String> attributeFilters) { Page<Iterator<Compliance>> result = this.getSnapshotIterator(targetDate, consumerUuids, ownerFilters, statusFilters, productNameFilters, subscriptionSkuFilters, subscriptionNameFilters, attributeFilters, null); return result.getPageData(); } /** * Retrieves an iterator over the compliance snapshots on the target date. * * @param targetDate * The date for which to retrieve compliance snapshots. If null, the current date will be used * instead. * * @param consumerUuids * A list of consumer UUIDs to use to filter the results. If provided, only compliances for * consumers in the list will be retrieved. * * @param ownerFilters * A list of owners to use to filter the results. If provided, only compliances for consumers * belonging to the specified owners (orgs) will be retrieved. * * @param statusFilters * A list of statuses to use to filter the results. If provided, only compliances with a status * matching the list will be retrieved. * * @param productNameFilters * A list of product names to use to filter compliances. If provided, only compliances for * consumers having installed the specified products will be retrieved. * * @param subscriptionSkuFilters * A list of subscription skus to use to filter compliances. If provided, only compliances for * the specified subscription skus will be retrieved. * * @param subscriptionNameFilters * A list of subscription names to use to filter compliances. If provided, only compliances for * the specified subscription names will be retrieved. * * @param attributeFilters * A map of entitlement attributes to use to filter compliances. If provided, only compliances * for entitlements having the specified values for the given attributes will be retrieved. * * @param pageRequest * A PageRequest instance containing paging information from the request. If null, no paging * will be performed. * * @return * A Page instance containing an iterator over the compliance snapshots for the target date and * the paging information for the query. */ @SuppressWarnings("checkstyle:indentation") public Page<Iterator<Compliance>> getSnapshotIterator(Date targetDate, List<String> consumerUuids, List<String> ownerFilters, List<String> statusFilters, List<String> productNameFilters, List<String> subscriptionSkuFilters, List<String> subscriptionNameFilters, Map<String, String> attributeFilters, PageRequest pageRequest) { Page<Iterator<Compliance>> page = new Page<Iterator<Compliance>>(); page.setPageRequest(pageRequest); DetachedCriteria subquery = DetachedCriteria.forClass(Compliance.class); subquery.createAlias("consumer", "c"); subquery.createAlias("c.consumerState", "state"); // https://hibernate.atlassian.net/browse/HHH-2776 if (consumerUuids != null && !consumerUuids.isEmpty()) { subquery.add(Restrictions.in("c.uuid", consumerUuids)); } Date toCheck = targetDate == null ? new Date() : targetDate; subquery.add( Restrictions.or(Restrictions.isNull("state.deleted"), Restrictions.gt("state.deleted", toCheck))); subquery.add(Restrictions.le("state.created", toCheck)); if (ownerFilters != null && !ownerFilters.isEmpty()) { subquery.createAlias("c.owner", "o"); subquery.add(Restrictions.in("o.key", ownerFilters)); } subquery.add(Restrictions.le("date", toCheck)); subquery.setProjection( Projections.projectionList().add(Projections.max("date")).add(Projections.groupProperty("c.uuid"))); Session session = this.currentSession(); Criteria query = session.createCriteria(Compliance.class, "comp").createAlias("comp.consumer", "cs") .add(Subqueries.propertiesIn(new String[] { "comp.date", "cs.uuid" }, subquery)) .setCacheMode(CacheMode.IGNORE).setReadOnly(true); if ((statusFilters != null && !statusFilters.isEmpty()) || (attributeFilters != null && attributeFilters.containsKey("management_enabled")) || (productNameFilters != null && !productNameFilters.isEmpty())) { query.createAlias("comp.status", "stat"); if (statusFilters != null && !statusFilters.isEmpty()) { query.add(Restrictions.in("stat.status", statusFilters)); } if (attributeFilters != null && attributeFilters.containsKey("management_enabled")) { boolean managementEnabledFilter = PropertyConverter .toBoolean(attributeFilters.get("management_enabled")); query.add(Restrictions.eq("stat.managementEnabled", managementEnabledFilter)); } if (productNameFilters != null && !productNameFilters.isEmpty()) { query.createAlias("stat.compliantProducts", "cprod", JoinType.LEFT_OUTER_JOIN) .createAlias("stat.partiallyCompliantProducts", "pcprod", JoinType.LEFT_OUTER_JOIN) .createAlias("stat.nonCompliantProducts", "ncprod", JoinType.LEFT_OUTER_JOIN); DetachedCriteria prodQuery = DetachedCriteria.forClass(Compliance.class, "comp2"); prodQuery.createAlias("comp2.consumer", "cons2"); prodQuery.createAlias("cons2.installedProducts", "installed"); prodQuery.add(Restrictions.and(Restrictions.in("installed.productName", productNameFilters), Restrictions.eqProperty("comp2.id", "comp.id"))); prodQuery.setProjection(Projections.property("installed.productId")); query.add(Restrictions.or(Property.forName("cprod.productId").in(prodQuery), Property.forName("pcprod.productId").in(prodQuery), Property.forName("ncprod.productId").in(prodQuery))); } } // Add subscription filters, if necessary if ((subscriptionSkuFilters != null && !subscriptionSkuFilters.isEmpty()) || (subscriptionNameFilters != null && !subscriptionNameFilters.isEmpty())) { // Impl note: We have to be very careful with alias names, as Hibernate has a tendancy // to errorneously truncate "long" ones. Actual property/field names are safe, though. query.createAlias("comp.entitlements", "entsnap"); if (subscriptionSkuFilters != null && !subscriptionSkuFilters.isEmpty()) { query.add(Restrictions.in("entsnap.productId", subscriptionSkuFilters)); } if (subscriptionNameFilters != null && !subscriptionNameFilters.isEmpty()) { query.add(Restrictions.in("entsnap.productName", subscriptionNameFilters)); } } if (pageRequest != null && pageRequest.isPaging()) { page.setMaxRecords(this.getRowCount(query)); query.setFirstResult((pageRequest.getPage() - 1) * pageRequest.getPerPage()); query.setMaxResults(pageRequest.getPerPage()); if (pageRequest.getSortBy() != null) { query.addOrder( pageRequest.getOrder() == PageRequest.Order.ASCENDING ? Order.asc(pageRequest.getSortBy()) : Order.desc(pageRequest.getSortBy())); } } page.setPageData(new AutoEvictingColumnarResultsIterator<Compliance>(session, query.scroll(ScrollMode.FORWARD_ONLY), 0)); return page; } /** * Retrieves an iterator over the compliance snapshots for the specified consumer. * * @param consumerUUID * The UUID for the consumer for which to retrieve compliance snapshots. * * @param startDate * The start date to use to filter snapshots retrieved. If specified, only snapshots occurring * after the start date, and the snapshot immediately preceding it, will be retrieved. * * @param endDate * The end date to use to filter snapshots retrieved. If specified, only snapshots occurring * before the end date will be retrieved. * * @return * An iterator over the snapshots for the specified consumer. */ public Iterator<Compliance> getSnapshotIteratorForConsumer(String consumerUUID, Date startDate, Date endDate) { Page<Iterator<Compliance>> result = this.getSnapshotIteratorForConsumer(consumerUUID, startDate, endDate, null); return result.getPageData(); } /** * Retrieves an iterator over the compliance snapshots for the specified consumer. * * @param consumerUUID * The UUID for the consumer for which to retrieve compliance snapshots. * * @param startDate * The start date to use to filter snapshots retrieved. If specified, only snapshots occurring * after the start date, and the snapshot immediately preceding it, will be retrieved. * * @param endDate * The end date to use to filter snapshots retrieved. If specified, only snapshots occurring * before the end date will be retrieved. * * @param pageRequest * A PageRequest instance containing paging information from the request. If null, no paging * will be performed. * * @return * A Page instance containing an iterator over the snapshots for the specified consumer, and * the paging information for the query. */ @SuppressWarnings("checkstyle:indentation") public Page<Iterator<Compliance>> getSnapshotIteratorForConsumer(String consumerUUID, Date startDate, Date endDate, PageRequest pageRequest) { Page<Iterator<Compliance>> page = new Page<Iterator<Compliance>>(); page.setPageRequest(pageRequest); Session session = this.currentSession(); Criteria query = session.createCriteria(Compliance.class, "comp1"); query.createAlias("comp1.consumer", "cons1"); query.add(Restrictions.eq("cons1.uuid", consumerUUID)); if (startDate != null) { DetachedCriteria subquery = DetachedCriteria.forClass(Compliance.class, "comp2"); subquery.createAlias("comp2.consumer", "cons2"); subquery.createAlias("cons2.consumerState", "state2"); subquery.add(Restrictions.or(Restrictions.isNull("state2.deleted"), Restrictions.gt("state2.deleted", startDate))); subquery.add(Restrictions.lt("state2.created", startDate)); subquery.add(Restrictions.eqProperty("cons2.uuid", "cons1.uuid")); subquery.add(Restrictions.lt("comp2.date", startDate)); subquery.setProjection(Projections.projectionList().add(Projections.max("comp2.date"))); query.add(Restrictions.disjunction().add(Restrictions.ge("comp1.date", startDate)) .add(Subqueries.propertyEq("comp1.date", subquery))); } if (endDate != null) { query.add(Restrictions.le("comp1.date", endDate)); } query.setCacheMode(CacheMode.IGNORE); query.setReadOnly(true); if (pageRequest != null && pageRequest.isPaging()) { page.setMaxRecords(this.getRowCount(query)); query.setFirstResult((pageRequest.getPage() - 1) * pageRequest.getPerPage()); query.setMaxResults(pageRequest.getPerPage()); if (pageRequest.getSortBy() != null) { query.addOrder( pageRequest.getOrder() == PageRequest.Order.ASCENDING ? Order.asc(pageRequest.getSortBy()) : Order.desc(pageRequest.getSortBy())); } } page.setPageData(new AutoEvictingColumnarResultsIterator<Compliance>(session, query.scroll(ScrollMode.FORWARD_ONLY), 0)); return page; } /** * Retrieves the compliance status counts over the given time span with the specified criteria. * The counts are returned in a map of maps, with the outer map mapping the dates to the inner * map which maps the statuses to their respective counts. * <p></p> * If the start and/or end dates are null, the time span will be similarly unrestricted. Note * that the time within a given Date object is ignored. If neither the start nor end dates are * provided, all known compliance status data will be used. * * @param startDate * The date at which the time span should begin. If null, all compliance statuses before the * end date (if provided) will be used. * * @param endDate * The date at which the time span should end. If null, all compliance statuses after the * start date (if provided) will be used. * * @param sku * A subscription sku to use to filter compliance status counts. If provided, only consumers * using the specified sku will be counted. * * @param subscriptionName * A subscription name to use to filter compliance status counts. If provided, only consumers * using subscriptions with the specified product name will be counted. * * @param attributes * A map of entitlement attributes to use to filter compliance status counts. If provided, only * consumers with entitlements having the specified values for the given attributes will be * counted. * * @param ownerKey * An owner key to use to filter compliance status counts. If provided, only consumers * associated with the specified owner key/account will be counted. * * @return * a map of maps containing the compliance status counts, grouped by day. If no counts were * found for the given time span, an empty map will be returned. */ public Map<Date, Map<String, Integer>> getComplianceStatusCounts(Date startDate, Date endDate, String ownerKey, List<String> consumerUuids, String sku, String subscriptionName, String productName, Map<String, String> attributes) { Page<Map<Date, Map<String, Integer>>> result = this.getComplianceStatusCounts(startDate, endDate, ownerKey, consumerUuids, sku, subscriptionName, productName, attributes, null); return result.getPageData(); } /** * Retrieves the compliance status counts over the given time span with the specified criteria. * The counts are returned in a map of maps, with the outer map mapping the dates to the inner * map which maps the statuses to their respective counts. * <p></p> * If the start and/or end dates are null, the time span will be similarly unrestricted. Note * that the time within a given Date object is ignored. If neither the start nor end dates are * provided, all known compliance status data will be used. * * @param startDate * The date at which the time span should begin. If null, all compliance statuses before the * end date (if provided) will be used. * * @param endDate * The date at which the time span should end. If null, all compliance statuses after the * start date (if provided) will be used. * * @param sku * A subscription sku to use to filter compliance status counts. If provided, only consumers * using the specified sku will be counted. * * @param subscriptionName * A subscription name to use to filter compliance status counts. If provided, only consumers * using subscriptions with the specified product name will be counted. * * @param productName * A product name to use to filter compliance status counts. If provided, only consumers with * an installed product with the specified product name will be counted. * * @param attributes * A map of entitlement attributes to use to filter compliance status counts. If provided, only * consumers with entitlements having the specified values for the given attributes will be * counted. * * @param ownerKey * An owner key to use to filter compliance status counts. If provided, only consumers * associated with the specified owner key/account will be counted. * * @param pageRequest * A PageRequest instance containing paging information from the request. If null, no paging * will be performed. * * @return * A page containing a map of maps containing the compliance status counts, grouped by day. If * no counts were found for the given time span, the page will contain an empty map. */ public Page<Map<Date, Map<String, Integer>>> getComplianceStatusCounts(Date startDate, Date endDate, String ownerKey, List<String> consumerUuids, String sku, String subscriptionName, String productName, Map<String, String> attributes, PageRequest pageRequest) { Page<Map<Date, Map<String, Integer>>> page = new Page<Map<Date, Map<String, Integer>>>(); page.setPageRequest(pageRequest); // Build our query... // Impl note: This query's results MUST be sorted by date in ascending order. If it's not, // the algorithm below breaks. Query query = this.buildComplianceStatusCountQuery(this.currentSession(), startDate, endDate, ownerKey, consumerUuids, sku, subscriptionName, productName, attributes); // Clamp our dates so they're no further out than "today." Date today = new Date(); if (startDate != null && startDate.after(today)) { startDate = today; } if (endDate != null && endDate.after(today)) { endDate = today; } // Execute & process results... Map<Date, Map<String, Integer>> resultmap = new TreeMap<Date, Map<String, Integer>>(); Map<String, Object[]> cstatusmap = new HashMap<String, Object[]>(); // Step through our data and do our manual aggregation bits... ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY); if (results.next()) { Calendar date = Calendar.getInstance(); Object[] row = results.get(); String uuid = (String) row[0]; row[1] = ((String) row[1]).toLowerCase(); date.setTime((Date) row[2]); // Prime the calendars here... Calendar cdate = Calendar.getInstance(); cdate.setTime(startDate != null ? startDate : date.getTime()); cdate.set(Calendar.HOUR_OF_DAY, 23); cdate.set(Calendar.MINUTE, 59); cdate.set(Calendar.SECOND, 59); cdate.set(Calendar.MILLISECOND, 999); Calendar end = Calendar.getInstance(); end.setTimeInMillis(endDate != null ? endDate.getTime() : Long.MAX_VALUE); for (; this.compareCalendarsByDate(cdate, end) <= 0; cdate.add(Calendar.DATE, 1)) { while (this.compareCalendarsByDate(date, cdate) <= 0) { // Date is before our current date. Store the uuid's status so we can add it to // our counts later. cstatusmap.put(uuid, row); if (!results.next()) { if (endDate == null) { end.setTimeInMillis(cdate.getTimeInMillis()); } break; } row = (Object[]) results.get(); uuid = (String) row[0]; row[1] = ((String) row[1]).toLowerCase(); date.setTime((Date) row[2]); } Date hashdate = cdate.getTime(); Map<String, Integer> statusmap = new HashMap<String, Integer>(); // Go through and add up all our counts for the day. for (Object[] cstatus : cstatusmap.values()) { if (cstatus[3] == null || this.compareDatesByDate(hashdate, (Date) cstatus[3]) < 0) { Integer count = statusmap.get((String) cstatus[1]); statusmap.put((String) cstatus[1], (count != null ? count + 1 : 1)); } } resultmap.put(hashdate, statusmap); } } results.close(); // Pagination // This is horribly inefficient, but the only way to do it with the current implementation. if (pageRequest != null && pageRequest.isPaging()) { page.setMaxRecords(resultmap.size()); int offset = (pageRequest.getPage() - 1) * pageRequest.getPerPage(); int nextpage = offset + pageRequest.getPerPage(); // Trim results. :( Iterator<Date> iterator = resultmap.keySet().iterator(); for (int pos = 0; iterator.hasNext(); ++pos) { iterator.next(); if (pos < offset || pos >= nextpage) { iterator.remove(); } } } page.setPageData(resultmap); return page; } /** * Builds the Query object to be used by the getComplianceStatusCounts method. * <p></p> * The Query object is constructed with HQL translated from the following SQL: * <p></p><pre> * SELECT * ConsumerState.uuid, * ComplianceStatusSnap.status, * ComplianceStatusSnap.date * * FROM * "gb_consumer_state" ConsumerState * * INNER JOIN "gb_consumer_snap" ConsumerSnap * ON ConsumerSnap.uuid = ConsumerState.uuid * * INNER JOIN "gb_compliance_snap" ComplianceSnap * ON ComplianceSnap.id = ConsumerSnap.compliance_snap_id * * INNER JOIN "gb_compliance_status_snap" ComplianceStatusSnap * ON ComplianceStatusSnap.compliance_snap_id = ComplianceSnap.id * * WHERE ( * ConsumerState.deleted IS NULL * * OR date_part('year', ComplianceSnap.date) < date_part('year', ConsumerState.deleted) * * OR ( * date_part('year', ComplianceSnap.date) = date_part('year', ConsumerState.deleted) * AND date_part('month', ComplianceSnap.date) < date_part('month', ConsumerState.deleted) * ) * * OR ( * date_part('year', ComplianceSnap.date) = date_part('year', ConsumerState.deleted) * AND date_part('month', ComplianceSnap.date) = date_part('month', ConsumerState.deleted) * AND date_part('day', ComplianceSnap.date) < date_part('day', ConsumerState.deleted) * ) * ) * * AND (ComplianceStatusSnap.date, ConsumerSnap.uuid) IN ( * SELECT * max(ComplianceSnap2.date) AS maxdate, * ConsumerState2.uuid * * FROM * "gb_consumer_state" ConsumerState2 * * INNER JOIN "gb_consumer_snap" ConsumerSnap2 * ON ConsumerSnap2.uuid = ConsumerState2.uuid * * INNER JOIN "gb_compliance_snap" ComplianceSnap2 * ON ComplianceSnap2.id = ConsumerSnap2.compliance_snap_id * * INNER JOIN "gb_compliance_status_snap" ComplianceStatusSnap2 * ON ComplianceStatusSnap2.compliance_snap_id = ComplianceSnap2.id * * GROUP BY * date_part('year', ComplianceSnap2.date), * date_part('month', ComplianceSnap2.date), * date_part('day', ComplianceSnap2.date), * ConsumerState2.uuid * ) * * -- Min date * AND ( * date_part('year', ComplianceSnap.date) > 2014 * * OR ( * date_part('year', ComplianceSnap.date) = 2014 * AND date_part('month', ComplianceSnap.date) > 11 * ) * * OR ( * date_part('year', ComplianceSnap.date) = 2014 * AND date_part('month', ComplianceSnap.date) = 11 * AND date_part('day', ComplianceSnap.date) >= 25 * ) * * OR (ComplianceStatusSnap.date, ConsumerSnap.uuid) IN ( * SELECT * max(ComplianceSnap3.date) AS maxdate, * ConsumerState3.uuid * * FROM * "gb_consumer_state" ConsumerState3 * * INNER JOIN "gb_consumer_snap" ConsumerSnap3 * ON ConsumerSnap3.uuid = ConsumerState3.uuid * * INNER JOIN "gb_compliance_snap" ComplianceSnap3 * ON ComplianceSnap3.id = ConsumerSnap3.compliance_snap_id * * INNER JOIN "gb_compliance_status_snap" ComplianceStatusSnap3 * ON ComplianceStatusSnap3.compliance_snap_id = ComplianceSnap3.id * * WHERE * date_part('year', ComplianceSnap3.date) < 2014 * * OR ( * date_part('year', ComplianceSnap3.date) = 2014 * AND date_part('month', ComplianceSnap3.date) < 11 * ) * * OR ( * date_part('year', ComplianceSnap3.date) = 2014 * AND date_part('month', ComplianceSnap3.date) = 11 * AND date_part('day', ComplianceSnap3.date) < 25 * ) * * GROUP BY * ConsumerState3.uuid * ) * ) * * -- Max date * AND ( * date_part('year', ComplianceSnap.date) < 2014 * * OR ( * date_part('year', ComplianceSnap.date) = 2014 * AND date_part('month', ComplianceSnap.date) < 11 * ) * * OR ( * date_part('year', ComplianceSnap.date) = 2014 * AND date_part('month', ComplianceSnap.date) = 11 * AND date_part('day', ComplianceSnap.date) <= 15 * ) * ) * * -- Checking for the SKU, Product Name or attributes * AND ComplianceSnap.id IN ( * SELECT ConsumerSnapI.compliance_snap_id * FROM "gb_consumer_snap" ConsumerSnapI * LEFT JOIN "gb_entitlement_snap" EntitlementSnap * ON EntitlementSnap.compliance_snap_id = ConsumerSnapI.compliance_snap_id * * LEFT JOIN "gb_ent_attr_snap" EntitlementAttributeSnap * ON EntitlementAttributeSnap.ent_snap_id = EntitlementSnap.id * * WHERE * ConsumerSnapI.uuid = ConsumerSnap.uuid * --AND ( * -- EntitlementSnap.product_id = User-input SKU * -- OR EntitlementSnap.product_name = User-input name (matches-like?) * -- OR ( * -- EntitlementAttributeSnap.gb_ent_attr_name = 'management_enabled' * -- AND EntitlementAttributeSnap.gb_ent_attr_value = 1 * -- ) * --) * ) * * ORDER BY * ComplianceStatusSnap.date ASC * </pre> * * @param session * The session to use to create the query. * * @param startDate * The date at which the time span should begin. If null, all compliance statuses before the * end date (if provided) will be used. * * @param endDate * The date at which the time span should end. If null, all compliance statuses after the * start date (if provided) will be used. * * @param sku * A subscription sku to use to filter compliance status counts. If provided, only consumers * using the specified sku will be counted. * * @param subscriptionName * A product name to use to filter compliance status counts. If provided, only consumers using * subscriptions which provide the specified product name will be counted. * * @param productName * A product name to use to filter compliance status counts. If provided, only consumers with * an installed product with the specified product name will be counted. * * @param attributes * A map of entitlement attributes to use to filter compliance status counts. If provided, only * consumers with entitlements having the specified values for the given attributes will be * counted. * * @param ownerKey * An owner key to use to filter compliance status counts. If provided, only consumers * associated with the specified owner key/account will be counted. * * @return * A Query object to be used for retrieving compliance status counts. */ @SuppressWarnings({ "checkstyle:methodlength", "checkstyle:indentation" }) private Query buildComplianceStatusCountQuery(Session session, Date startDate, Date endDate, String ownerKey, List<String> consumerUuids, String sku, String subscriptionName, String productName, Map<String, String> attributes) { List<Object> parameters = new LinkedList<Object>(); int counter = 0; StringBuilder hql = new StringBuilder("SELECT " + "ConsumerState.uuid," + "ComplianceStatusSnap.status," + "ComplianceStatusSnap.date," + "ConsumerState.deleted " + "FROM " + "Consumer AS ConsumerSnap " + "INNER JOIN ConsumerSnap.consumerState AS ConsumerState " + "INNER JOIN ConsumerSnap.complianceSnapshot AS ComplianceSnap " + "INNER JOIN ComplianceSnap.status AS ComplianceStatusSnap " + "LEFT JOIN ComplianceSnap.entitlements AS EntitlementSnap " + "WHERE (" + "ConsumerState.deleted IS NULL " + "OR year(ComplianceSnap.date) < year(ConsumerState.deleted) " + "OR (" + "year(ComplianceSnap.date) = year(ConsumerState.deleted) " + "AND month(ComplianceSnap.date) < month(ConsumerState.deleted) " + ") " + "OR (" + "year(ComplianceSnap.date) = year(ConsumerState.deleted) " + "AND month(ComplianceSnap.date) = month(ConsumerState.deleted) " + "AND day(ComplianceSnap.date) < day(ConsumerState.deleted)" + ")" + ") " + "AND (ComplianceStatusSnap.date, ConsumerSnap.uuid) IN (" + "SELECT " + "max(ComplianceSnap2.date) AS maxdate, " + "ConsumerState2.uuid " + "FROM " + "Consumer AS ConsumerSnap2 " + "INNER JOIN ConsumerSnap2.consumerState AS ConsumerState2 " + "INNER JOIN ConsumerSnap2.complianceSnapshot AS ComplianceSnap2 " + "INNER JOIN ComplianceSnap2.status AS ComplianceStatusSnap2 " + "GROUP BY " + "year(ComplianceSnap2.date)," + "month(ComplianceSnap2.date)," + "day(ComplianceSnap2.date)," + "ConsumerState2.uuid " + ") "); // Add our reporting criteria... if (sku != null || subscriptionName != null || (attributes != null && attributes.size() > 0) || ownerKey != null || (consumerUuids != null && consumerUuids.size() > 0) || productName != null) { List<String> criteria = new LinkedList<String>(); StringBuffer inner = new StringBuffer("AND ("); // TODO: // Owner, SKU, product name and should be replaced by the same mechanism we used for // --matches in Subscription-manager. if (ownerKey != null) { criteria.add("ConsumerState.ownerKey = ?" + ++counter); parameters.add(ownerKey); } if (sku != null) { criteria.add("EntitlementSnap.productId = ?" + ++counter); parameters.add(sku); } if (subscriptionName != null) { criteria.add("EntitlementSnap.productName = ?" + ++counter); parameters.add(subscriptionName); } if (productName != null) { criteria.add(String.format("?%d IN (" + "SELECT Installed.productName " + "FROM " + "Consumer AS ConsumerSnapP " + "INNER JOIN ConsumerSnapP.installedProducts AS Installed " + "INNER JOIN ConsumerSnapP.complianceSnapshot AS ComplianceSnapP " + "INNER JOIN ComplianceSnapP.status AS ComplianceStatusSnapP " + "LEFT JOIN ComplianceStatusSnapP.compliantProducts AS CProduct " + "LEFT JOIN ComplianceStatusSnapP.nonCompliantProducts AS NCProduct " + "LEFT JOIN ComplianceStatusSnapP.partiallyCompliantProducts AS PCProduct " + "WHERE " + "ComplianceStatusSnapP.id = ComplianceStatusSnap.id " + "AND (" + "Installed.productId = CProduct.productId " + "OR Installed.productId = NCProduct.productId " + "OR Installed.productId = PCProduct.productId " + ")" + ")", ++counter)); parameters.add(productName); } if (attributes != null && attributes.size() > 0) { if (attributes.containsKey("management_enabled")) { boolean managementEnabledFilter = PropertyConverter .toBoolean(attributes.get("management_enabled")); criteria.add("ComplianceStatusSnap.managementEnabled = ?" + ++counter); parameters.add(managementEnabledFilter); // Don't process this attribute as part of entitlement attributes, // as it has already been handled. attributes.remove("management_enabled"); } for (Map.Entry<String, String> entry : attributes.entrySet()) { criteria.add(String.format( "(?%d, ?%d) IN (" + "SELECT ENTRY(EntitlementSnapA.attributes) " + "FROM Entitlement AS EntitlementSnapA " + "WHERE EntitlementSnapA.id = EntitlementSnap.id" + ")", ++counter, ++counter)); parameters.add(entry.getKey()); parameters.add(entry.getValue()); } } if (consumerUuids != null && consumerUuids.size() > 0) { criteria.add("ConsumerState.uuid IN (?" + ++counter + ")"); parameters.add(consumerUuids); } // Append the criteria to our where clause and close it. Iterator<String> ci = criteria.iterator(); inner.append(ci.next()); while (ci.hasNext()) { inner.append(" AND "); inner.append(ci.next()); } hql.append(inner.append(") ")); } // Add our date range, if necessary... if (startDate != null) { int year = startDate.getYear() + 1900; int month = startDate.getMonth() + 1; int day = startDate.getDate(); hql.append(String.format("AND (" + "year(ComplianceStatusSnap.date) > ?%1$d " + "OR (" + "year(ComplianceStatusSnap.date) = ?%1$d " + "AND month(ComplianceStatusSnap.date) > ?%2$d" + ") " + "OR (" + "year(ComplianceStatusSnap.date) = ?%1$d " + " AND month(ComplianceStatusSnap.date) = ?%2$d " + " AND day(ComplianceStatusSnap.date) >= ?%3$d" + ") " + "OR (ComplianceStatusSnap.date, ConsumerSnap.uuid) IN (" + "SELECT " + "max(ComplianceStatusSnap2.date) AS maxdate, " + "ConsumerState2.uuid " + "FROM " + "Consumer AS ConsumerSnap2 " + "INNER JOIN ConsumerSnap2.consumerState AS ConsumerState2 " + "INNER JOIN ConsumerSnap2.complianceSnapshot AS ComplianceSnap2 " + "INNER JOIN ComplianceSnap2.status AS ComplianceStatusSnap2 " + "WHERE " + "year(ComplianceStatusSnap2.date) < ?%1$d " + "OR (" + "year(ComplianceStatusSnap2.date) = ?%1$d " + "AND month(ComplianceStatusSnap2.date) < ?%2$d" + ") " + "OR (" + "year(ComplianceStatusSnap2.date) = ?%1$d " + "AND month(ComplianceStatusSnap2.date) = ?%2$d " + "AND day(ComplianceStatusSnap2.date) < ?%3$d" + ") " + "GROUP BY " + "ConsumerState2.uuid" + ")" + ") ", ++counter, ++counter, ++counter)); parameters.add(year); parameters.add(month); parameters.add(day); } if (endDate != null) { int year = endDate.getYear() + 1900; int month = endDate.getMonth() + 1; int day = endDate.getDate(); hql.append(String.format("AND (" + "year(ComplianceStatusSnap.date) < ?%1$d " + "OR (" + "year(ComplianceStatusSnap.date) = ?%1$d " + "AND month(ComplianceStatusSnap.date) < ?%2$d " + ") " + "OR (" + "year(ComplianceStatusSnap.date) = ?%1$d " + "AND month(ComplianceStatusSnap.date) = ?%2$d " + "AND day(ComplianceStatusSnap.date) <= ?%3$d" + ")" + ") ", ++counter, ++counter, ++counter)); parameters.add(year); parameters.add(month); parameters.add(day); } // Add our grouping... hql.append("ORDER BY ComplianceStatusSnap.date ASC"); // log.debug("\nFINAL QUERY: {}", hql.toString()); // Build our query object and set the parameters... Query query = session.createQuery(hql.toString()); query.setReadOnly(true); for (int i = 1; i <= counter; ++i) { Object param = parameters.remove(0); if (param instanceof Collection) { query.setParameterList(String.valueOf(i), (Collection) param); } else { query.setParameter(String.valueOf(i), param); } } return query; } /** * Compares the date, without time, represented by the two Calendar objects. * <p></p> * <strong>Note:</strong><br/> * This method <em>does not</em> check that the calendars provided are not null. Passing a null * calendar will result in a NullPointerException. * * @param cal1 * A Calendar instance to compare * * @param cal2 * A Calendar instance to compare * * @return * 0 if the dates are equal, a negative value if the date represented by cal1 is before cal2's, * or a positive value if cal1's date is after cal2's. */ private int compareCalendarsByDate(Calendar cal1, Calendar cal2) { int year = cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR); int month = cal1.get(Calendar.MONTH) - cal2.get(Calendar.MONTH); int date = cal1.get(Calendar.DATE) - cal2.get(Calendar.DATE); return (year != 0 ? year : (month != 0 ? month : date)); } /** * Compares the date, without time, represented by the two Date objects. * <p></p> * <strong>Note:</strong><br/> * This method <em>does not</em> check that the dates provided are not null. Passing a null date * will result in a NullPointerException. * * @param date1 * A Date instance to compare * * @param date2 * A Date instance to compare * * @return * 0 if the dates are equal, a negative value if the date represented by date1 is before * date2's, or a positive value if date1's date is after date2's. */ private int compareDatesByDate(Date date1, Date date2) { int year = date1.getYear() - date2.getYear(); int month = date1.getMonth() - date2.getMonth(); int date = date1.getDate() - date2.getDate(); return (year != 0 ? year : (month != 0 ? month : date)); } /** * Deletes any compliances older than the specified duration in minutes. * * @param minutes * The number of minutes to use for cleaning up compliances * * @return * the number of compliance snapshots deleted during the cleanup operation */ @Transactional public int cleanupCompliances(int minutes) { Calendar cal = Calendar.getInstance(); cal.add(Calendar.MINUTE, -1 * minutes); int batchSize = 256; int count = 0; // Build our JPA query... EntityManager em = this.getEntityManager(); CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Compliance> cquery = builder.createQuery(Compliance.class); Root<Compliance> croot = cquery.from(Compliance.class); ParameterExpression<Date> param = builder.parameter(Date.class); cquery.select(croot).where(builder.lessThanOrEqualTo(croot.<Date>get("date"), param)); TypedQuery<Compliance> query = em.createQuery(cquery); query.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS); query.setHint("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS); query.setParameter(param, cal.getTime()); query.setMaxResults(batchSize); // Repeatedly execute it so long as we're getting the batch size back List<Compliance> compliances; do { compliances = (List<Compliance>) query.getResultList(); for (Compliance compliance : compliances) { em.remove(compliance); } em.flush(); count += compliances.size(); } while (compliances.size() == batchSize); return count; } }