org.hyperic.hq.measurement.agent.server.TrackerThread.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.measurement.agent.server.TrackerThread.java

Source

/*
 * NOTE: This copyright does *not* cover user programs that use HQ
 * 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, 2005, 2006], Hyperic, Inc.
 * This file is part of HQ.
 * 
 * HQ 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.agent.server;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;

import org.hyperic.hq.agent.server.AgentStartException;
import org.hyperic.hq.agent.server.AgentStorageException;
import org.hyperic.hq.agent.server.AgentStorageProvider;
import org.hyperic.hq.bizapp.client.AgentCallbackClientException;
import org.hyperic.hq.bizapp.client.MeasurementCallbackClient;
import org.hyperic.hq.bizapp.client.StorageProviderFetcher;
import org.hyperic.hq.measurement.data.TrackEventReport;
import org.hyperic.hq.product.ConfigTrackPluginManager;
import org.hyperic.hq.product.LogTrackPluginManager;
import org.hyperic.hq.product.TrackEvent;
import org.hyperic.hq.product.TrackEventPluginManager;

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

class TrackerThread implements Runnable {

    private static final String PROP_MAXEVENTBATCHSIZE = "agent.eventReportBatchSize";

    private static final int MAX_EVENT_BATCHSIZE = 100;
    private static final String CONFIGTRACK_LISTNAME = "configtrack_spool";
    private static final String LOGTRACK_LISTNAME = "logtrack_spool";
    private static final int LOGTRACK_RECSIZE = 5120;

    private volatile boolean shouldDie;
    private volatile Thread myThread;

    private Object interrupter;
    private long waitTime;
    private int maxEventBatchSize = MAX_EVENT_BATCHSIZE;

    private AgentStorageProvider storage;
    private MeasurementCallbackClient client;

    private ConfigTrackPluginManager ctManager;
    private LogTrackPluginManager ltManager;

    private Log log;

    TrackerThread(ConfigTrackPluginManager ctManager, LogTrackPluginManager ltManager, AgentStorageProvider storage,
            Properties bootProps) throws AgentStartException {
        this.ctManager = ctManager;
        this.ltManager = ltManager;
        this.storage = storage;
        this.shouldDie = false;
        this.myThread = null;
        this.interrupter = new Object();
        this.client = setupClient();
        this.waitTime = TrackEventPluginManager.DEFAULT_INTERVAL;
        this.log = LogFactory.getLog(TrackerThread.class);
        String info = bootProps.getProperty(CONFIGTRACK_LISTNAME);
        if (info != null) {
            storage.addOverloadedInfo(CONFIGTRACK_LISTNAME, info);
        }
        info = bootProps.getProperty(LOGTRACK_LISTNAME);
        if (info != null) {
            storage.addOverloadedInfo(LOGTRACK_LISTNAME, info);
        }

        // Create list early since we want a larger recordsize than the default of 1k.
        try {
            this.storage.createList(LOGTRACK_LISTNAME, LOGTRACK_RECSIZE);
        } catch (AgentStorageException ignore) {
            // Most likely an agent update where the existing spool
            // already exists.  Will fall back to the old 1k size.
        }

        String sMaxBatchSize = bootProps.getProperty(PROP_MAXEVENTBATCHSIZE);

        if (sMaxBatchSize != null) {
            try {
                this.maxEventBatchSize = Integer.parseInt(sMaxBatchSize);
            } catch (NumberFormatException exc) {
                throw new AgentStartException(
                        PROP_MAXEVENTBATCHSIZE + " is not " + "a valid integer ('" + sMaxBatchSize + "')");
            }

        }

        this.log.info("Event report batch size set to " + this.maxEventBatchSize);
    }

    private void interruptMe() {
        synchronized (this.interrupter) {
            this.interrupter.notify();
        }
    }

    private MeasurementCallbackClient setupClient() throws AgentStartException {
        StorageProviderFetcher fetcher;

        fetcher = new StorageProviderFetcher(this.storage);
        return new MeasurementCallbackClient(fetcher);
    }

    private void flushEvents(LinkedList events, String dListName) {
        if (events.isEmpty()) {
            return;
        }

        for (Iterator i = events.iterator(); i.hasNext();) {
            TrackEvent event = (TrackEvent) i.next();

            try {
                String data = event.encode();
                this.storage.addToList(dListName, data);
                this.log.debug("Stored event (" + data.length() + " bytes) " + event);
            } catch (Exception e) {
                this.log.error("Unable to store data", e);
            }
        }
    }

    private void flushLogTrackEvents() {
        LinkedList events = ltManager.getEvents();
        flushEvents(events, LOGTRACK_LISTNAME);
    }

    private void flushConfigTrackEvents() {
        LinkedList events = ctManager.getEvents();
        flushEvents(events, CONFIGTRACK_LISTNAME);
    }

    private void processDlist(String dListName) {
        boolean moreEventsToProcess = true;

        while (moreEventsToProcess) {
            moreEventsToProcess = processNextEventReport(dListName, maxEventBatchSize);
        }

        try {
            this.storage.flush();
        } catch (AgentStorageException exc) {
            this.log.error("Failed to flush agent storage", exc);
        }
    }

    /**
     * Process the next event report.
     * 
     * @param dListName
     * @param batchSize The event batch size per report.
     * @return <code>true</code> if there are more events to process.
     */
    private boolean processNextEventReport(String dListName, int batchSize) {
        boolean moreEventsToProcess = false;

        Iterator i = this.storage.getListIterator(dListName);

        if (i == null) {
            return false;
        }

        TrackEventReport report = new TrackEventReport();
        int numEventsProcessed = 0;

        while (numEventsProcessed <= batchSize && i.hasNext()) {
            TrackEvent event;

            try {
                event = TrackEvent.decode((String) i.next());
            } catch (Exception e) {
                this.log.error("Unable to decode record -- deleting: " + e);
                continue;
            } finally {
                numEventsProcessed++;
                moreEventsToProcess = i.hasNext();
            }

            this.log.debug("Adding event to report=" + event);

            report.addEvent(event);
        }

        if (!sendReportToServer(dListName, report)) {
            // If there is an error sending to the server, try again later.
            moreEventsToProcess = false;
            numEventsProcessed = 0;
        }

        removeProcessedEventsFromStorage(dListName, numEventsProcessed);

        return moreEventsToProcess;
    }

    private boolean sendReportToServer(String dListName, TrackEventReport report) {

        boolean succeeded = true;

        if (report.getEvents().length > 0) {
            this.log.debug("Sending report to server");

            try {
                if (dListName.equals(LOGTRACK_LISTNAME)) {
                    this.client.trackSendLog(report);
                } else if (dListName.equals(CONFIGTRACK_LISTNAME)) {
                    this.client.trackSendConfigChange(report);
                } else {
                    throw new IllegalArgumentException("Unknown DList name");
                }

                this.log.debug("Completed sending report to server");
            } catch (AgentCallbackClientException e) {
                this.log.error("Error sending report to server: " + e);
                succeeded = false;
            }
        }

        return succeeded;
    }

    private void removeProcessedEventsFromStorage(String dListName, int numEventsProcessed) {
        for (Iterator i = this.storage.getListIterator(dListName); numEventsProcessed > 0 && i != null
                && i.hasNext(); numEventsProcessed--) {
            i.next();
            i.remove();
        }
    }

    public void die() {
        // Before exiting, make sure we flush any outstanding events.
        flushLogTrackEvents();
        flushConfigTrackEvents();

        this.shouldDie = true;
        this.interruptMe();
    }

    public void run() {
        this.myThread = Thread.currentThread();

        while (this.shouldDie == false) {
            // Flush log track events
            flushLogTrackEvents();

            // Flush config track events
            flushConfigTrackEvents();

            // Process the log track dlist
            processDlist(LOGTRACK_LISTNAME);

            // Process the config track dlist
            processDlist(CONFIGTRACK_LISTNAME);

            synchronized (this.interrupter) {
                try {
                    this.interrupter.wait(waitTime);
                } catch (InterruptedException e) {
                }
            }
        }
    }
}