package org.zstack.core.tacker;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.CoreGlobalProperty;
import org.zstack.core.Platform;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusSteppingCallback;
import org.zstack.core.thread.PeriodicTask;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.header.Component;
import org.zstack.header.message.MessageReply;
import org.zstack.header.message.NeedReplyMessage;
import org.zstack.utils.DebugUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
*/
public abstract class PingTracker implements Component {
public abstract String getResourceName();
public abstract NeedReplyMessage getPingMessage(String resUuid);
public abstract int getPingInterval();
public abstract int getParallelismDegree();
public abstract void handleReply(String resourceUuid, MessageReply reply);
private final static CLogger logger = Utils.getLogger(PingTracker.class);
private final List<String> resourceUuids = Collections.synchronizedList(new ArrayList<String>());
private Set<String> resourceInTracking = Collections.synchronizedSet(new HashSet<String>());
private Future<Void> trackerThread = null;
@Autowired
protected CloudBus bus;
@Autowired
protected ThreadFacade thdf;
private class Tracker implements PeriodicTask {
@Override
public TimeUnit getTimeUnit() {
return TimeUnit.SECONDS;
}
@Override
public long getInterval() {
return getPingInterval();
}
@Override
public String getName() {
return String.format("pingTracker-for-%s-managementNode-%s", getResourceName(), Platform.getManagementServerId());
}
@Override
public void run() {
try {
List<NeedReplyMessage> msgs = null;
synchronized (resourceUuids) {
final Map<NeedReplyMessage, String> tmp = new HashMap<NeedReplyMessage, String>();
msgs = new ArrayList<NeedReplyMessage>();
for (String resUuid : resourceUuids) {
if (resourceInTracking.contains(resUuid)) {
continue;
}
NeedReplyMessage msg = getPingMessage(resUuid);
msgs.add(msg);
resourceInTracking.add(resUuid);
tmp.put(msg, resUuid);
}
if (msgs.isEmpty()) {
return;
}
bus.send(msgs, getParallelismDegree(), new CloudBusSteppingCallback(null) {
@Override
public void run(NeedReplyMessage msg, MessageReply reply) {
String resUuid = tmp.get(msg);
DebugUtils.Assert(resUuid!=null, "where is my resource uuid???");
try {
handleReply(resUuid, reply);
} finally {
resourceInTracking.remove(resUuid);
}
}
});
}
} catch (Throwable t) {
logger.warn("unhandled throwable", t);
}
}
}
protected void trackHook(String resourceUuid) {
}
protected void untrackHook(String resourceUuid) {
}
protected void startHook() {
}
protected void pingIntervalChanged() {
startTracker();
}
public void track(String resUuid) {
synchronized (resourceUuids) {
if (!resourceUuids.contains(resUuid)) {
resourceUuids.add(resUuid);
trackHook(resUuid);
logger.debug(String.format("start tracking %s[uuid:%s]", getResourceName(), resUuid));
}
}
}
public void untrackAll() {
synchronized (resourceUuids) {
resourceUuids.clear();
logger.debug(String.format("untrack all %s", getResourceName()));
}
}
public void untrack(String resUuid) {
synchronized (resourceUuids) {
resourceUuids.remove(resUuid);
untrackHook(resUuid);
logger.debug(String.format("stop tracking %s[uuid:%s]", getResourceName(), resUuid));
}
}
public void track(Collection<String> resUuids) {
synchronized (resourceUuids) {
for (String resUuid : resUuids) {
if (!resourceUuids.contains(resUuid)) {
resourceUuids.add(resUuid);
trackHook(resUuid);
logger.debug(String.format("start tracking %s[uuid:%s]", getResourceName(), resUuid));
}
}
}
}
public void untrack(Collection<String> resUuids) {
synchronized (resourceUuids) {
for (String resUuid : resUuids) {
resourceUuids.remove(resUuid);
untrackHook(resUuid);
logger.debug(String.format("stop tracking %s[uuid:%s]", getResourceName(), resUuid));
}
}
}
protected void startTracker() {
if (trackerThread != null) {
trackerThread.cancel(true);
}
if (CoreGlobalProperty.UNIT_TEST_ON) {
trackerThread = thdf.submitPeriodicTask(new Tracker(), getPingInterval());
} else {
trackerThread = thdf.submitPeriodicTask(new Tracker(), getPingInterval() + new Random().nextInt(30));
}
}
@Override
public boolean start() {
startTracker();
startHook();
return true;
}
@Override
public boolean stop() {
trackerThread.cancel(true);
return true;
}
}