/*
* 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-2011], VMWare, 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.server.session;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.agent.server.session.AgentDataTransferJob;
import org.hyperic.hq.agent.server.session.AgentSynchronizer;
import org.hyperic.hq.appdef.Agent;
import org.hyperic.hq.appdef.shared.AgentManager;
import org.hyperic.hq.appdef.shared.AgentNotFoundException;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.authz.shared.ResourceManager;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.common.shared.TransactionRetry;
import org.hyperic.hq.hibernate.SessionManager;
import org.hyperic.hq.hibernate.SessionManager.SessionRunner;
import org.hyperic.hq.measurement.shared.MeasurementProcessor;
import org.hyperic.hq.measurement.shared.SRNManager;
import org.hyperic.hq.stats.ConcurrentStatsCollector;
import org.hyperic.hq.zevents.Zevent;
import org.hyperic.hq.zevents.ZeventEnqueuer;
import org.hyperic.hq.zevents.ZeventListener;
import org.hyperic.util.stats.StatCollector;
import org.hyperic.util.stats.StatUnreachableException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This class is used to schedule and unschedule metrics for a given entity. The
* schedule operation is synchronized to throttle rescheduling.
*/
@Component
public class AgentScheduleSynchronizer {
private final Log log = LogFactory.getLog(AgentScheduleSynchronizer.class);
@Autowired
private ZeventEnqueuer zEventManager;
@Autowired
private AgentManager agentManager;
@Autowired
private ResourceManager resourceManager;
@Autowired
private ConcurrentStatsCollector concurrentStatsCollector;
@Autowired
private AgentSynchronizer agentSynchronizer;
@Autowired
private TransactionRetry transactionRetry;
@Autowired
private SRNManager srnManager;
@Autowired
private MeasurementProcessor measurementProcessor;
private final Map<Integer, Collection<AppdefEntityID>> scheduleAeids =
new HashMap<Integer, Collection<AppdefEntityID>>();
private final Map<Integer, Collection<AppdefEntityID>> unscheduleAeids =
new HashMap<Integer, Collection<AppdefEntityID>>();
@PostConstruct
void initialize() {
ZeventListener<Zevent> l = getScheduleListener();
concurrentStatsCollector.register(new StatCollector() {
public long getVal() throws StatUnreachableException {
synchronized (unscheduleAeids) {
return unscheduleAeids.size();
}
}
public String getId() {
return ConcurrentStatsCollector.UNSCHEDULE_QUEUE_SIZE;
}
});
concurrentStatsCollector.register(new StatCollector() {
public long getVal() throws StatUnreachableException {
synchronized (scheduleAeids) {
return scheduleAeids.size();
}
}
public String getId() {
return ConcurrentStatsCollector.SCHEDULE_QUEUE_SIZE;
}
});
zEventManager.addBufferedListener(AgentScheduleSyncZevent.class, l);
zEventManager.addBufferedListener(AgentUnscheduleZevent.class, l);
}
public void unschedule(String agentToken, Collection<AppdefEntityID> aeids) {
if (aeids == null || aeids.isEmpty()) {
return;
}
final Integer agentId = getAgentId(agentToken);
if (agentId == null) {
return;
}
synchronized (unscheduleAeids) {
Collection<AppdefEntityID> c = unscheduleAeids.get(agentId);
if (c == null) {
unscheduleAeids.put(agentId, aeids);
} else {
c.addAll(aeids);
}
}
addScheduleJob(false, agentId);
}
private Integer getAgentId(String agentToken) {
try {
// will throw an AgentNotFoundException if the agent is not found
return agentManager.getAgent(agentToken).getId();
} catch (AgentNotFoundException e) {
log.debug(e,e);
return null;
}
}
public Collection<AppdefEntityID> unscheduleNonEntities(String agentToken, Collection<AppdefEntityID> aeids) {
if (aeids == null || aeids.isEmpty()) {
return Collections.emptyList();
}
final boolean debug = log.isDebugEnabled();
final Collection<AppdefEntityID> toUnschedule = new HashSet<AppdefEntityID>();
final Integer agentId = getAgentId(agentToken);
if (agentId == null) {
return Collections.emptyList();
}
for (final AppdefEntityID aeid : aeids) {
if (null == resourceManager.findResource(aeid)) {
toUnschedule.add(aeid);
if (debug) log.debug("unscheduling non-entity=" + aeid);
}
}
if (!toUnschedule.isEmpty()) {
synchronized (unscheduleAeids) {
Collection<AppdefEntityID> c = unscheduleAeids.get(agentId);
if (c == null) {
unscheduleAeids.put(agentId, aeids);
} else {
c.addAll(aeids);
}
}
addScheduleJob(false, agentId);
}
return new ArrayList<AppdefEntityID>(toUnschedule);
}
private ZeventListener<Zevent> getScheduleListener() {
return new ZeventListener<Zevent>() {
public void processEvents(List<Zevent> events) {
final List<AppdefEntityID> toSchedule = new ArrayList<AppdefEntityID>(events.size());
final Map<String, Collection<AppdefEntityID>> unscheduleMap =
new HashMap<String, Collection<AppdefEntityID>>(events.size());
final boolean debug = log.isDebugEnabled();
for (final Zevent z : events) {
if (z instanceof AgentScheduleSyncZevent) {
AgentScheduleSyncZevent event = (AgentScheduleSyncZevent) z;
toSchedule.addAll(event.getEntityIds());
if (debug) log.debug("Schduling eids=[" + event.getEntityIds() + "]");
} else if (z instanceof AgentUnscheduleZevent) {
AgentUnscheduleZevent event = (AgentUnscheduleZevent) z;
String token = event.getAgentToken();
if (token == null) {
continue;
}
Collection<AppdefEntityID> tmp;
if (null == (tmp = unscheduleMap.get(token))) {
tmp = new HashSet<AppdefEntityID>();
unscheduleMap.put(token, tmp);
}
tmp.addAll(event.getEntityIds());
if (debug) log.debug("Unschduling eids=[" + event.getEntityIds() + "]");
}
}
final Map<Integer, Collection<AppdefEntityID>> agentAppdefIds =
agentManager.getAgentMap(toSchedule);
synchronized (scheduleAeids) {
for (final Map.Entry<Integer, Collection<AppdefEntityID>> entry : agentAppdefIds.entrySet()) {
final Integer agentId = entry.getKey();
final Collection<AppdefEntityID> eids = entry.getValue();
Collection<AppdefEntityID> tmp;
if (null == (tmp = scheduleAeids.get(agentId))) {
tmp = new HashSet<AppdefEntityID>(eids.size());
scheduleAeids.put(agentId, tmp);
}
tmp.addAll(eids);
addScheduleJob(true, agentId);
}
}
synchronized (unscheduleAeids) {
for (Map.Entry<String, Collection<AppdefEntityID>> entry : unscheduleMap.entrySet()) {
final String token = entry.getKey();
final Collection<AppdefEntityID> eids = entry.getValue();
Integer agentId;
try {
agentId = agentManager.getAgent(token).getId();
} catch (AgentNotFoundException e) {
log.warn("Could not get agentToken=" + token +
" from db to unschedule: " + e);
continue;
}
Collection<AppdefEntityID> tmp;
if (null == (tmp = unscheduleAeids.get(agentId))) {
tmp = new HashSet<AppdefEntityID>(eids.size());
unscheduleAeids.put(agentId, tmp);
}
tmp.addAll(eids);
addScheduleJob(false, agentId);
}
}
}
public String toString() {
return "AgentScheduleSyncListener";
}
};
}
private void addScheduleJob(final boolean schedule, final Integer agentId) {
if (agentId == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("adding " + (schedule ? "schedule" : "unschedule") + " job for agentId=" + agentId);
}
final AgentDataTransferJob job = new AgentDataTransferJob() {
private Collection<AppdefEntityID> aeids;
private AtomicBoolean success = new AtomicBoolean(false);
public String toString() {
return getJobDescription() + ", agentId=" + agentId;
}
public String getJobDescription() {
if (schedule) {
return "Agent Schedule Job";
} else {
return "Agent UnSchedule Job";
}
}
public int getAgentId() {
return agentId;
}
public void execute() {
final Map<Integer, Collection<AppdefEntityID>> aeidMap =
(schedule) ? scheduleAeids : unscheduleAeids;
synchronized (aeidMap) {
aeids = aeidMap.remove(agentId);
}
if (aeids != null && !aeids.isEmpty()) {
runSchedule(schedule, agentId, aeids);
}
if (Thread.currentThread().isInterrupted()) {
return;
}
success.set(true);
}
public void onFailure(String reason) {
log.warn("could not schedule aeids=" + aeids + " to agentId=" + agentId + ": " + reason);
}
public boolean wasSuccessful() {
return success.get();
}
};
agentSynchronizer.addAgentJob(job);
}
private void runSchedule(final boolean schedule, final Integer agentId,
final Collection<AppdefEntityID> aeids) {
final Runnable runner = new Runnable() {
public void run() {
_runSchedule(schedule, agentId, aeids);
}
};
transactionRetry.runTransaction(runner, 3, 1000);
}
private void _runSchedule(final boolean schedule, final Integer agentId,
final Collection<AppdefEntityID> aeids) {
try {
SessionManager.runInSession(new SessionRunner() {
public void run() throws Exception {
final Agent agent = agentManager.getAgent(agentId);
if (agent == null) {
return;
}
if (schedule) {
if (log.isDebugEnabled()) {
log.debug("scheduling " + aeids.size() + " resources to agentid=" +
agent.getId());
}
srnManager.schedule(aeids, false, true);
} else {
if (log.isDebugEnabled()) {
log.debug("unscheduling " + aeids.size() + " resources to agentid=" +
agent.getId());
}
measurementProcessor.unschedule(agent.getAgentToken(), aeids);
}
}
public String getName() {
if (schedule) {
return "Schedule";
} else {
return "Unschedule";
}
}
});
} catch (Exception e) {
throw new SystemException(e);
}
}
}