org.hyperic.hq.measurement.server.session.AvailabilityFallbackCheckQue.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.measurement.server.session.AvailabilityFallbackCheckQue.java

Source

/*
 * NOTE: This copyright does *not* cover user programs that use Hyperic
 * program services by normal system calls through the application
 * program interfaces provided as part of the Hyperic Plug-in Development
 * Kit or the Hyperic Client Development Kit - this is merely considered
 * normal use of the program, and does *not* fall under the heading of
 *  "derived work".
 *
 *  Copyright (C) [2004-2013], VMware, Inc.
 *  This file is part of Hyperic.
 *
 *  Hyperic is free software; you can redistribute it and/or modify
 *  it under the terms version 2 of the GNU General Public License 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 for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 *  USA.
 */

package org.hyperic.hq.measurement.server.session;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * An object that manages a que of Platform IDs for platforms for which fallback availability check needs to be done.
 * Every X seconds, a FallbackChecker polls N platform IDs, checks for their availability, and re-adds them to the Que
 * 
 * <BR><B><U>Que flow:</U></B>
 * <BR><B>addToQue</B> - Platform ID is added to the platformsRecheckQue, if it's not yet there.
 * <BR><B>poll</B> - PlatformID moves from platformsRecheckQue to platformsRecheckInProgress member.
 * <BR><B>beforeDataUpdate</B> - before availability data is about to be stored in the DB/Cache, the que is updated:
 * <BR>    - if this is an update from the server: 
 * <BR>        If PlatformID exists in platformsPendingQueRemoval - it is removed from all the members/
 * <BR>    Otherwise: PlatformID is moved back from platformsRecheckInProgress into platformsRecheckQue 
 *             to be rechecked after the next interval.
 *     <BR>- if this is an update from the agent: 
 *     <BR>    if currently PlatformID is not processed (it is not in platformsRecheckInProgress) - it is removed from all Que members.
 *     <BR>     Otherwise: PlatformID is added to platformsPendingQueRemoval, and will be cleaned once the recheck process ends.
 * 
 * @author amalia
 *
 */
public class AvailabilityFallbackCheckQue {

    // TODO: following code review:
    // minimize checks here since these are synchronized updates of availability data.
    // HowTo? Remove the need for platformsRecheckInProgress: Add LastTimeUpdate to the ResourceDataPoint. This is the lastTimeStamp that we know
    // of when starting to calculate availability for a Platform. before availMgr updates data,  
    // where it checks for timestamps mismatches, it can check if the the LastTimeUpdate is before the actual last timestamp in the cache, and if so -
    // remove the platform from the que, and not update it.

    private final Log log = LogFactory.getLog(AvailabilityFallbackCheckQue.class);

    // a que holding the platforms pending recheck
    private final ConcurrentLinkedQueue<ResourceDataPoint> platformsRecheckQue;

    // Map:PlatformID->latest availability ResourceDataPoint>, for quick-access of platformsRecheckQue items. 
    private final Map<Integer, ResourceDataPoint> currentPlatformsInQue;

    // Set of Platform IDs, for platforms whose status is currently checked by AvailabilityFallbackChecker.
    private final Set<Integer> platformsRecheckInProgress;

    // Set of Platform IDs, for platforms currently rechecked, which agent information was sent.
    private final Set<Integer> platformsPendingQueRemoval;

    // Map: MeasurementID->PlatformID for quick-access
    private final Map<Integer, Integer> measurementIdToPlatformId;

    // -----------------------------------------------------------------------------------
    // -----------------------------------------------------------------------------------

    /**
     * Default CTor
     */
    public AvailabilityFallbackCheckQue() {
        this.platformsRecheckQue = new ConcurrentLinkedQueue<ResourceDataPoint>();
        this.currentPlatformsInQue = new HashMap<Integer, ResourceDataPoint>();
        this.platformsRecheckInProgress = new HashSet<Integer>();
        this.platformsPendingQueRemoval = new HashSet<Integer>();
        this.measurementIdToPlatformId = new HashMap<Integer, Integer>();
    }

    public void clearQue() {
        log.info("Clearing availability check queues.");
        this.platformsRecheckQue.clear();
        this.currentPlatformsInQue.clear();
        this.platformsRecheckInProgress.clear();
        this.platformsPendingQueRemoval.clear();
        this.measurementIdToPlatformId.clear();
    }

    /**
     * @return the number of Platforms pending availability status check
     */
    public synchronized int getSize() {
        return this.platformsRecheckQue.size();
    }

    // -----------------------------------------------------------------------------------
    // -----------------------------------------------------------------------------------

    /**
     * Add a single platform to the rechecks que.
     * @param platformId - the platform to be checked.
     * @param dataPoint - the latest status associated with the platform
     * @return true, if added. false, if the platform is already in the que and will not be re-added.
     */
    public synchronized boolean addToQue(Integer platformId, ResourceDataPoint dataPoint) {
        if (log.isDebugEnabled()) {
            log.debug("addToQue: start resourcePlatformId=" + platformId + ", curQueSize: " + getSize());
        }
        if (this.currentPlatformsInQue.containsKey(platformId)) {
            // fix for jira issue HQ-4325 - keep the existing platform down data
            // point timestamp updated to the last availability check time
            ResourceDataPoint existingDataPoint = this.currentPlatformsInQue.get(platformId);
            existingDataPoint.getMetricValue().setTimestamp(dataPoint.getTimestamp());
            // in case there was an agent update in the middle, we do not want to remove it from the recheck que.
            //this.platformsPendingQueRemoval.remove(platformId);
            return false;
        }
        this.platformsRecheckQue.add(dataPoint);
        this.currentPlatformsInQue.put(platformId, dataPoint);
        this.measurementIdToPlatformId.put(dataPoint.getMeasurementId(), platformId);
        return true;
    }

    /**
     * Adds a collection of platforms to the rechecks que. Platforms that already exist in the que will not be re-added.
     * @param platformsToAdd - map:PlatformID->latest status associated with the platform
     * @return the number of platforms actually added.
     */
    public synchronized int addToQue(Map<Integer, ResourceDataPoint> platformsToAdd) {
        int res = 0;
        for (Integer platformId : platformsToAdd.keySet()) {
            boolean added = addToQue(platformId, platformsToAdd.get(platformId));
            res += added ? 1 : 0;
        }
        if (log.isDebugEnabled()) {
            log.debug("addToQue: added " + res);
        }
        return res;
    }

    /**
     * Poll a platform to check availability for,
     * @return the next ResourceDataPoint in the que. The ResourceDataPoint is the latest status of the checked platform. Return null if the que is empty.
     */
    public synchronized ResourceDataPoint poll() {
        if (log.isDebugEnabled()) {
            log.debug("poll: start, que size: " + getSize());
        }
        ResourceDataPoint res = this.platformsRecheckQue.poll();
        if (res != null) {
            Integer platformId = res.getResource().getId();
            this.platformsRecheckInProgress.add(platformId);
        }
        return res;
    }

    /**
     * A method to be called before storing availability status updates.
     * <BR> This method updates the que and filters the given dataPoints list, so that agent updates will always override server calculations.
     * <BR> In case the update is from the agent:
     * <BR>     - the returned list is identical to the input list.
     * <BR>     - all platforms that are about to be updated are removed from the que (or added to the PendingRemoval list, if check is in progress).
     * <BR> In case the update is from the server:
     * <BR>     - if the platform are in the PendingRemoval list - they are removed completely from the Que, and not inserted into the result.
     * <BR>     - Otherwise - they are added into the result, and platforms are re-inserted into the recheckQue.
     * <BR> <B> Notes: </B>
     * <BR> - Case 1: agent updated platform status which recheck was done. In this case, the platform's descendants will not be added into the dataPoints
     * input since the checker will not add descendants with updated availability status. In case it will: the descendant status will remain as "UNKNOWN" until 
     * the next agent status update (half-an-interval or so).
     * <BR>    - Case 2: Over-checking: A platform is checked and then re-inserted to the que. 
     * <BR> In case of threads working on the same que, we may get over-checking, instead of checking every 2 minutes or so.
     * <BR> If we start working with several threads we need to use 2 queues intermittently - all threads poll from Que1 until the que is empty, 
     * every checked platform is inserted into Que2. In the next checks-round - poll from Que2 and insert into Que1.
     * @param dataPoints - a collection of availability datapoints about to be added.
     * @param isUpdateFromServer - true, if the server fallback checks asked for the updated, false if the agent updated with the status.
     * @return a filtered collection of datapoints, the ones that should be updated in the DB/cache
     */
    public synchronized Collection<DataPoint> beforeDataUpdate(Collection<DataPoint> dataPoints,
            boolean isUpdateFromServer) {
        Collection<DataPoint> res = new ArrayList<DataPoint>();
        Collection<DataPoint> removed = new ArrayList<DataPoint>();
        final boolean debug = log.isDebugEnabled();
        for (DataPoint dataPoint : dataPoints) {
            Integer measurementId = dataPoint.getMeasurementId();
            Integer resourceId = this.measurementIdToPlatformId.get(measurementId);
            if (resourceId == null) {
                // we do not know this resource as a platformId. one of:
                // 1. this is not a platform - update it.
                // 2. this is a platform that is not in the recheck que - update it.
                res.add(dataPoint);
                continue;
            }
            Integer platformId = resourceId;
            if (isUpdateFromServer) {
                this.platformsRecheckInProgress.remove(platformId);
                if (this.platformsPendingQueRemoval.contains(platformId)) {
                    // this should not be updated from the server.
                    this.platformsPendingQueRemoval.remove(platformId);
                    ResourceDataPoint platformDataPoint = this.currentPlatformsInQue.get(platformId);
                    this.platformsRecheckQue.remove(platformDataPoint);
                    this.measurementIdToPlatformId.remove(measurementId);
                    this.currentPlatformsInQue.remove(platformId);
                    if (debug) {
                        removed.add(dataPoint);
                    }
                } else {
                    res.add(dataPoint);
                    ResourceDataPoint queDataPoint = this.currentPlatformsInQue.get(platformId);
                    ResourceDataPoint pointToAddToQue = new ResourceDataPoint(queDataPoint.getResource(),
                            dataPoint);
                    this.currentPlatformsInQue.remove(platformId);
                    addToQue(platformId, pointToAddToQue);
                }
            } else {
                // update from agent
                res.add(dataPoint);
                removeFromQueBeforeUpdateFromAgent(platformId);
            }
        }
        if (isUpdateFromServer) {
            if (debug) {
                log.debug("beforeDataUpdate from Server: updating: " + res.size() + " out of " + dataPoints.size()
                        + ", removed=" + removed);
            }
        }
        return res;
    }

    // -----------------------------------------------------------------------------------
    // -----------------------------------------------------------------------------------

    // return platform Id
    private synchronized Integer removeFromCurrentPlatformsInQue(ResourceDataPoint dp) {
        Integer platformId = null;
        for (Entry<Integer, ResourceDataPoint> entry : currentPlatformsInQue.entrySet()) {
            if (dp.equals(entry.getValue())) {
                platformId = entry.getKey();
                break;
            }
        }

        if (platformId != null) {
            currentPlatformsInQue.remove(platformId);
        }
        return platformId;
    }

    // return measurement Id
    private synchronized Integer removeFromMeasIdToPlatId(Integer platformId) {
        Integer measId = null;
        for (Entry<Integer, Integer> entry : measurementIdToPlatformId.entrySet()) {
            if (platformId.equals(entry.getValue())) {
                measId = entry.getKey();
                break;
            }
        }

        if (measId != null) {
            measurementIdToPlatformId.remove(measId);
        }
        return measId;
    }

    public synchronized int cleanQueFromNonExistant() {
        int res = 0;
        Collection<ResourceDataPoint> pointsToDel = new ArrayList<ResourceDataPoint>();
        for (ResourceDataPoint dp : this.platformsRecheckQue) {
            if ((dp.getResource() == null) || (dp.getResource().isInAsyncDeleteState())) {
                res++;
                pointsToDel.add(dp);
                Integer platformId = removeFromCurrentPlatformsInQue(dp);
                if (platformId != null) {
                    removeFromMeasIdToPlatId(platformId);
                }
            }
        }
        platformsRecheckQue.removeAll(pointsToDel);
        if ((res != 0) && log.isDebugEnabled()) {
            log.debug("cleanQueFromNonExistant: removing " + res + " platforms.");
        }
        return res;
    }

    public synchronized int removeFromQue(Collection<Integer> platformResourceIds) {
        int res = 0;
        final boolean debug = log.isDebugEnabled();
        for (Integer platformId : platformResourceIds) {
            if (this.platformsRecheckInProgress.contains(platformId)) {
                if (debug) {
                    log.debug("0 adding to pending queue platformId=" + platformId + ", curQueSize: " + getSize());
                }
                this.platformsPendingQueRemoval.add(platformId);
                continue;
            }
            ResourceDataPoint platformDataPoint = this.currentPlatformsInQue.get(platformId);
            if (platformDataPoint == null) {
                // this point is not in the que.
                continue;
            }
            //else 
            this.platformsRecheckQue.remove(platformDataPoint);
            this.currentPlatformsInQue.remove(platformId);
            Integer measId = platformDataPoint.getMeasurementId();
            this.measurementIdToPlatformId.remove(measId);
            res++;
        }
        return res;
    }

    private synchronized boolean removeFromQueBeforeUpdateFromAgent(Integer platformId) {
        if (this.platformsRecheckInProgress.contains(platformId)) {
            if (log.isDebugEnabled()) {
                log.debug(
                        "adding to pending queue removal platformId=" + platformId + ", curQueSize: " + getSize());
            }
            this.platformsPendingQueRemoval.add(platformId);
            return true;
        }

        ResourceDataPoint platformDataPoint = this.currentPlatformsInQue.get(platformId);
        if (platformDataPoint == null) {
            // this point is not in the que.
            return false;
        }
        //else 
        this.platformsRecheckQue.remove(platformDataPoint);
        this.currentPlatformsInQue.remove(platformId);
        Integer measId = platformDataPoint.getMeasurementId();
        this.measurementIdToPlatformId.remove(measId);

        return true;
    }

}