/*******************************************************************************
* Copyright (c) 2014 Imperial College London
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Raul Castro Fernandez - initial API and implementation
******************************************************************************/
package uk.ac.imperial.lsds.seep.infrastructure.monitor.master;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.joda.time.DateTime;
import org.joda.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.Observable;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.Stoppable;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.comm.serialization.MetricsDeserializer;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.comm.serialization.MetricsTuple;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.evaluate.AbstractEvaluator;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.metric.MetricName;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.metric.MetricValue;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.util.MetricReading;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.util.MetricReadingProvider;
/**
* Worker for the monitor master. Each worker serves a particular monitor slave
* (there should be one monitor slave per operator). The worker will endlessly
* receive metric tuples over a socket and evaluate
* @author mrouaux
*/
public class MonitorMasterWorker
implements Runnable, Stoppable, Observable<MonitorMasterListener> {
final private Logger logger = LoggerFactory.getLogger(MonitorMasterWorker.class);
private Socket slaveSocket;
private MetricsDeserializer deserializer;
private AbstractEvaluator evaluator;
private Map<Integer, MonitorMasterListener> listeners;
private boolean receive;
/**
* Convenience constructor
* @param slaveSocket Socket between the monitor master and a slave. The slave
* will stream metric readings over this connection at regular intervals.
* @param evaluator Evaluator for rules
*/
public MonitorMasterWorker(final Socket slaveSocket,
final MetricsDeserializer deserializer,
final AbstractEvaluator evaluator) {
this.slaveSocket = slaveSocket;
this.deserializer = deserializer;
this.evaluator = evaluator;
this.receive = true;
this.listeners = new HashMap<Integer, MonitorMasterListener>();
}
@Override
public void run() {
logger.info("Starting MonitorMasterWork for slave "
+ slaveSocket.getRemoteSocketAddress());
while(receive) {
logger.debug("Waiting for tuple from monitoring slave");
// Receive message from slave and deserialise metric tuple
final MetricsTuple tuple = deserializer.deserialize();
if (tuple != null) {
logger.debug("Received tuple from slave " + tuple.toString());
// Use policy evaluator to evaluate the all rules
evaluator.evaluate(new MetricReadingProvider() {
/**
* Operator identifier for the slave that sent the current tuple.
*/
@Override
public int getOperatorId() {
return tuple.getOperatorId();
}
/**
* Construct a reading from the received tuple and return it to
* the policy evaluator, which will make a decision based on it
* (and past history from previous readings).
*/
@Override
public MetricReading nextReading() {
MetricReading reading = new MetricReading();
Instant timestamp = DateTime.now().toInstant();
Map<MetricName, MetricValue> values
= new HashMap<MetricName, MetricValue>();
Set<MetricName> names = tuple.metricNames();
for(MetricName name : names) {
values.put(name, tuple.getMetricValue(name));
}
reading.setTimestamp(timestamp);
reading.setValues(values);
return reading;
}
});
// Notify any listeners regiestered for the operator
if (listeners.containsKey(tuple.getOperatorId())) {
listeners.get(tuple.getOperatorId()).onTupleReceived(tuple);
}
} else {
// A null tuple indicates that the counter-part worker on the slave
// has been closed and no further tuples will be sent by the slave.
try {
slaveSocket.close();
} catch (IOException ex) {
}
receive = false;
}
}
logger.info("MonitorMasterWorker is stopped");
}
@Override
public void stop() {
logger.info("Stopping MonitorMasterWorker");
receive = false;
}
/**
* Register listener to be notified whenever a tuple is received for a particular
* operator identifier.
*
* @param listener A MonitorMasterListener instance.
*/
@Override
public void addListener(MonitorMasterListener listener) {
listeners.put(listener.getOperatorId(), listener);
}
}