/*******************************************************************************
* 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.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.BinaryMetricsDeserializer;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.comm.serialization.MetricsDeserializer;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.PolicyRules;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.evaluate.AbstractEvaluator;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.evaluate.PolicyRulesEvaluator;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.util.InfrastructureAdaptor;
/**
* Monitoring master.
*
* @author mrouaux
*/
public class MonitorMaster implements Runnable, Stoppable, Observable<MonitorMasterListener> {
final private Logger logger = LoggerFactory.getLogger(MonitorMaster.class);
private InfrastructureAdaptor infrastructure;
private PolicyRules rules;
private int port;
private boolean listen;
private List<MonitorMasterWorker> workers;
private Map<Integer, MonitorMasterListener> listeners;
private ServerSocket serverSocket;
/**
* Convenience constructor.
* @param infrastructure
*/
public MonitorMaster(final InfrastructureAdaptor infrastructure,
final PolicyRules rules, final int port) {
this.infrastructure = infrastructure;
this.rules = rules;
this.port = port;
this.listen = true;
this.workers = new ArrayList<MonitorMasterWorker>();
this.listeners = new HashMap<Integer, MonitorMasterListener>();
}
/**
* Execute the monitoring master process.
*/
@Override
public void run() {
listen = true;
try {
logger.info("Starting MonitorMaster on port " + port);
serverSocket = new ServerSocket(port);
while(listen) {
Socket slaveSocket = null;
try {
slaveSocket = serverSocket.accept();
} catch(SocketException ex) {
if (!serverSocket.isClosed()) {
logger.error("Exception accepting connection from slave ", ex);
}
}
if ((slaveSocket != null) && (!serverSocket.isClosed())) {
logger.info("Received connection request from "
+ slaveSocket.getRemoteSocketAddress());
MetricsDeserializer binaryDeserializer = new BinaryMetricsDeserializer();
binaryDeserializer.initialize(slaveSocket.getInputStream());
AbstractEvaluator policyRulesEvaluator
= createEvaluator(infrastructure, rules);
MonitorMasterWorker worker = createMasterWorker(slaveSocket,
binaryDeserializer, policyRulesEvaluator);
startMasterWorker(worker);
// Keep a list of all workers so we can stop them when the server is stopped.
workers.add(worker);
}
}
// The server is stopping, we need to pass on the stop signal to all
// workers that were created before, in order to terminate gracefully
for(MonitorMasterWorker worker : workers) {
worker.stop();
}
serverSocket.close();
logger.info("MonitorMaster is stopped");
} catch(IOException ex){
logger.error("Exception reading ", ex);
}
}
/**
* Stops the monitoring master process.
*/
@Override
public void stop() {
logger.info("Stopping MonitorMaster");
listen = false;
try {
serverSocket.close();
} catch (IOException ex) {
logger.error("Exception closing MonitorMaster server socket", ex);
}
}
/**
* Creates an evaluator for the policy rules, acting upon the infrastructure
* proxied by the adaptor instance.
* @param adaptor Adaptor for the underlying infrastructure.
* @param rules Scaling policy rules to be evaluated by the evaluator.
* @return PolicyRulesEvaluator instance.
*/
protected AbstractEvaluator createEvaluator(final InfrastructureAdaptor adaptor,
final PolicyRules rules) {
return new PolicyRulesEvaluator(rules, adaptor);
}
/**
* Creates worker instance for the MonitorMaster, associated to a particular
* slave instance.
* @param slaveSocket Socket connected to a monitor slave.
* @param deserializer Deserializer to apply to received MetricsTuple messages.
* @param evaluator Evaluator to evaluate scaling policy rules.
* @return MonitorMasterWorker instance.
*/
protected MonitorMasterWorker createMasterWorker(final Socket slaveSocket,
final MetricsDeserializer deserializer, final AbstractEvaluator evaluator) {
MonitorMasterWorker worker
= new MonitorMasterWorker(slaveSocket, deserializer, evaluator);
// Copy all listeners from the master to the new worker
for(Entry<Integer, MonitorMasterListener> entry : listeners.entrySet()) {
worker.addListener(entry.getValue());
}
return worker;
}
protected void startMasterWorker(MonitorMasterWorker worker) {
new Thread(worker).start();
}
@Override
public void addListener(MonitorMasterListener listener) {
listeners.put(listener.getOperatorId(), listener);
}
}