/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package at.ac.tuwien.dsg.cloud.elise.conductor.listener;
import at.ac.tuwien.dsg.cloud.elise.collector.CollectorSettings.ConductorConfiguration;
import at.ac.tuwien.dsg.cloud.elise.collectorinterfaces.UnitInstanceCollector;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.GlobalIdentification;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.LocalIdentification;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.UnitInstance;
import at.ac.tuwien.dsg.cloud.elise.model.wrapper.UnitInstanceWrapper;
import at.ac.tuwien.dsg.cloud.salsa.domainmodels.types.ServiceCategory;
import at.ac.tuwien.dsg.cloud.salsa.messaging.model.Elise.EliseQueryProcessNotification;
import at.ac.tuwien.dsg.cloud.salsa.messaging.messageInterface.MessageClientFactory;
import at.ac.tuwien.dsg.cloud.salsa.messaging.messageInterface.MessagePublishInterface;
import at.ac.tuwien.dsg.cloud.salsa.messaging.messageInterface.MessageSubscribeInterface;
import at.ac.tuwien.dsg.cloud.salsa.messaging.messageInterface.SalsaMessageHandling;
import at.ac.tuwien.dsg.cloud.elise.collectorinterfaces.models.CollectorDescription;
import at.ac.tuwien.dsg.cloud.elise.collectorinterfaces.models.ConductorDescription;
import at.ac.tuwien.dsg.cloud.salsa.messaging.protocol.EliseQueueTopic;
import at.ac.tuwien.dsg.cloud.salsa.messaging.protocol.SalsaMessage;
import at.ac.tuwien.dsg.cloud.salsa.messaging.protocol.SalsaMessageTopic;
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.slf4j.Logger;
/**
* This class listen to the request from other elise and execute the command
*
* @author Duc-Hung Le
*
*/
public class ConductorListener {
static Logger logger = ConductorConfiguration.logger;
static Set<Class<? extends UnitInstanceCollector>> instanceCollectorClasses = new HashSet<>();
static URLClassLoader urlClassLoader = null;
static MessageClientFactory factory = MessageClientFactory.getFactory(ConductorConfiguration.getBroker(), ConductorConfiguration.getBrokerType());
public static void main(String[] args) {
logger.info("Conductor is starting. Generated ID: " + ConductorConfiguration.getConductorID());
// register conductor
// registerConductor();
// loading all the collectors
logger.debug("Loading all the jar...");
instanceCollectorClasses = loadAllJar();
if (instanceCollectorClasses == null || instanceCollectorClasses.isEmpty()) {
logger.debug("No collector class is load at the begining !");
} else {
for (Class<? extends UnitInstanceCollector> c : instanceCollectorClasses) {
logger.debug("Collector class found: {}", c.getName());
}
}
// Listening to the query
logger.debug("Subscribing to the control topic : {}", EliseQueueTopic.QUERY_TOPIC);
MessageSubscribeInterface subscriber = factory.getMessageSubscriber(new SalsaMessageHandling() {
Map<String, String> answeredElises = new HashMap();
@Override
public void handleMessage(SalsaMessage message) {
String fromElise = message.getFromSalsa();
String feedbackTopic = message.getFeedbackTopic();
logger.debug("Retrieve the request from ELISE: " + fromElise + ", feedback topic: " + feedbackTopic);
if (feedbackTopic != null) {
if (feedbackTopic.equals(this.answeredElises.get(fromElise))) {
logger.debug("Neglect duplicated subscribing message from ELISE: " + fromElise + ", topic: " + feedbackTopic);
return;
}
this.answeredElises.put(fromElise, feedbackTopic);
}
logger.debug("Listenner got a control message from ELISE of type: " + message.getMsgType());
String response;
SalsaMessage resMsg = null;
switch (message.getMsgType()) {
case discover:
register();
case elise_queryManyInstances:
case elise_querySingleInstance: {
// first send a notification to say that this ELISE is processing the info
MessagePublishInterface publish = factory.getMessagePublisher();
String queryUUID = message.getFeedbackTopic().substring(message.getFeedbackTopic().lastIndexOf(".") + 1);
publish.pushMessage(new SalsaMessage(SalsaMessage.MESSAGE_TYPE.elise_queryProcessNotification, ConductorConfiguration.getConductorID(), EliseQueueTopic.NOTIFICATION_TOPIC, null,
new EliseQueryProcessNotification(queryUUID, message.getFromSalsa(), ConductorConfiguration.getConductorID(), EliseQueryProcessNotification.QueryProcessStatus.PROCESSING).toJson()));
Set<UnitInstance> instances = null;
if (message.getMsgType().equals(SalsaMessage.MESSAGE_TYPE.elise_queryManyInstances)) {
logger.debug("Start to run all collector to collect instances with query string: {}", message.getPayload());
instances = runAllUnitInstanceCollector(message.getPayload(), false);
} else if (message.getMsgType().equals(SalsaMessage.MESSAGE_TYPE.elise_querySingleInstance)) {
logger.debug("Start to run all collector to collect instance with id: {}", message.getPayload());
instances = runAllUnitInstanceCollector(message.getPayload(), true);
}
if (instances == null) {
logger.error("Error happens when run all collector !");
break;
}
UnitInstanceWrapper wrapper = new UnitInstanceWrapper(instances);
response = wrapper.toJson();
logger.debug("This conductor got {} instances, wrapped !", instances.size());
resMsg = new SalsaMessage(SalsaMessage.MESSAGE_TYPE.elise_instanceInfoUpdate, ConductorConfiguration.getConductorID(), feedbackTopic, null, response);
break;
}
case elise_queryProvider:
response = "{\"message\":\"Not support query provider yet\"}";
break;
case elise_instanceInfoUpdate:
return;
case elise_addCollector:
resMsg = null;
CollectorDescription collector = CollectorDescription.fromJson(message.getPayload());
if (collector.getAssignedConductorID().equals(ConductorConfiguration.getConductorID())) {
try {
URL url = new URL(collector.getArtifactURL());
File folder = new File(ConductorConfiguration.getCollectorFolder(collector.getName()));
folder.mkdirs();
File file = new File(ConductorConfiguration.getCollectorFolder(collector.getName()) + "/" + collector.getName() + "-collector.jar");
logger.debug("Ok, now downloading artifact of collector {}, from: {}, save to: {}", collector.getName(), url.toString(), file.getAbsolutePath());
FileUtils.copyURLToFile(url, file);
// create configuration file
if (collector.getConfigurations() != null && !collector.getConfigurations().isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String s : collector.getConfigurations()) {
logger.debug("Found a configuration: {}", s);
sb.append(s).append("\n");
}
File adaptorConf = new File(ConductorConfiguration.getCollectorFolder(collector.getName()) + "/adaptor.conf");
FileUtils.writeStringToFile(adaptorConf, sb.toString());
}
} catch (MalformedURLException ex) {
logger.error("The URL to download collector artifact is incorrect");
ex.printStackTrace();
break;
} catch (IOException ex) {
logger.error("Cannot download artifact, or cannot copy to {}", ConductorConfiguration.getCollectorFolder(collector.getName()));
ex.printStackTrace();
break;
}
}
// try to load all Jar again
logger.debug("Trying to reload all collector ...");
instanceCollectorClasses = loadAllJar();
if (instanceCollectorClasses == null || instanceCollectorClasses.isEmpty()) {
logger.debug("No collector class is load at the begining !");
} else {
logger.debug("All collector load done !");
for (Class<? extends UnitInstanceCollector> c : instanceCollectorClasses) {
logger.debug(" --> Collector class found: {}", c.getName());
}
}
break;
default:
response = "{\"error\":\"Unknown Elise command !\"}";
break;
}
if (resMsg != null) {
MessagePublishInterface publisher = factory.getMessagePublisher();
logger.debug("Pushing answer data...");
publisher.pushMessage(resMsg);
} else {
logger.warn("Do not know how to reply the request, so resMsg is null !MsgType: {}", message.getMsgType());
}
}
});
subscriber.subscribe(EliseQueueTopic.QUERY_TOPIC);
logger.debug("Registering with salsa-engine...");
register();
logger.info("Conductor initiation is done. ID: {}", ConductorConfiguration.getConductorID());
}
private static void register() {
logger.debug(" --- Basic conductDesp start");
String response;
SalsaMessage resMsg = null;
ConductorDescription conductDesp = new ConductorDescription(ConductorConfiguration.getConductorID(), ConductorConfiguration.getELISE_IP());
logger.debug(" --- Basic conductDesp done");
if (instanceCollectorClasses != null) {
for (Class<? extends UnitInstanceCollector> c : instanceCollectorClasses) {
try {
logger.debug(" --- Trying to create instance");
UnitInstanceCollector u = (UnitInstanceCollector) urlClassLoader.loadClass(c.getName()).newInstance();
logger.debug(" --- Trying to create instance DONE");
for (String s : u.readAllAdaptorConfig()) {
logger.debug(" ALL-CONFIG-ARRAY: {}", s);
}
logger.debug(" --- Trying to create instance and get all config DONE");
conductDesp.hasCollector(new CollectorDescription(u.getName(), ConductorConfiguration.getConductorID(), "N/A", u.readAllAdaptorConfigOneString()));
logger.debug("When creating conductor description, we have collector: {}", u.getName());
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
logger.error("Cannot create instance of class: {}", c.toString(), ex);
}
}
}
response = conductDesp.toJson();
logger.debug("Sending back the conductor description: {}", response);
resMsg = new SalsaMessage(SalsaMessage.MESSAGE_TYPE.elise_conductorActivated, ConductorConfiguration.getConductorID(), SalsaMessageTopic.PIONEER_REGISTER_AND_HEARBEAT, "", response);
logger.debug("Sending message: " + resMsg.toJson());
MessagePublishInterface publisher = factory.getMessagePublisher();
logger.debug("Pushing registration data for conductor...");
publisher.pushMessage(resMsg);
}
// private static void registerConductor() {
// logger.info("Registering the collector: " + ConductorConfiguration.getConductorID());
//
// MessagePublishInterface publish = factory.getMessagePublisher();
// // TODO: Later, publish more detail about the conductor, e.g. list of collector
// SalsaMessage msg = new SalsaMessage(SalsaMessage.MESSAGE_TYPE.discover, ConductorConfiguration.getConductorID(), EliseQueueTopic.NOTIFICATION_TOPIC, "", "");
// publish.pushMessage(msg);
// logger.info("Registering message is published, not sure an ELISE can received it ! Note: we need to unicast, or ID checking.");
// }
/**
* Query all the instances Note: as conductor runs within a single thread, the whole collection can be block if the collector is not proper implemented
*
* @param queryOrDomainID
* @param isSingleInstanceQuery
* @return
*/
private static Set<UnitInstance> runAllUnitInstanceCollector(String queryOrDomainID, boolean isSingleInstanceQuery) {
logger.debug("Execute all the collectors...");
Set<UnitInstance> unitInstances = new HashSet<>();
for (Class<? extends UnitInstanceCollector> c : instanceCollectorClasses) {
try {
logger.debug("Create collector object from class: {}", c.getName());
UnitInstanceCollector collector = c.newInstance();
if (collector != null) {
logger.debug("Now we have the collector: {}, classname:{}", collector.getName(), collector.getClass().getName());
} else {
logger.error("No no, the class {} cannot be initiated", c.getName());
continue;
}
if (isSingleInstanceQuery) {
logger.debug("Calling collection for single ID");
unitInstances.add(collector.collectInstanceByID(queryOrDomainID));
} else {
logger.debug("Calling collection for all instances");
try {
Set<UnitInstance> tmpVar = collector.collectAllInstance();
logger.debug("All the collection has done !");
if (tmpVar != null && !tmpVar.isEmpty()) {
for (UnitInstance i : tmpVar) {
System.out.println("Adding unit instance ID for: " + i.getName() + "/" + i.getUuid());
LocalIdentification si = collector.identify(i);
GlobalIdentification gi = new GlobalIdentification(); // the instance contain a globalID, but null uuid and has 1 localID
gi.addLocalIdentification(si);
i.setIdentification(gi);
}
unitInstances.addAll(tmpVar);
} else {
logger.debug("Collector {} do not have any instance !", collector.getName());
}
} catch (Exception e) {
logger.debug("Error !");
logger.error(e.getMessage(), e);
logger.debug("End Error Message!");
e.printStackTrace();
}
}
} catch (InstantiationException | IllegalAccessException ex) {
logger.error("Error when initiate the class instance for collector. Class name: {}", c.getClass().getName(), ex);
}
}
return unitInstances;
// String queryURL = EliseConfiguration.getRESTEndpointLocal() + "/unitinstance/query";
// WebClient client = WebClient.create(queryURL);
// logger.debug("Querying to local REST: " + queryURL);
//
// HTTPConduit conduit = WebClient.getConfig(client).getHttpConduit();
//
// conduit.getClient().setReceiveTimeout(600000L); // maximum a collector collect data is 10 min, blocking
// conduit.getClient().setConnectionTimeout(30000L);
// return (String) client.accept(new String[]{"application/json"}).type("application/json").post(query, String.class);
}
public void sendAllData(UnitInstanceCollector aCollector) {
logger.debug("Inside the sending data method ...");
UnitInstanceWrapper instances = new UnitInstanceWrapper(aCollector.collectAllInstance());
logger.debug("Prepare to send items, Number: " + instances.getUnitInstances().size());
for (UnitInstance i : instances.getUnitInstances()) {
System.out.println("Adding local identification for instance: " + i.getName() + "/" + i.getUuid());
LocalIdentification si = aCollector.identify(i);
GlobalIdentification gi = new GlobalIdentification(); // the instance contain a globalID, but null uuid and has 1 localID
gi.addLocalIdentification(si);
i.setIdentification(gi);
}
MessagePublishInterface publish = factory.getMessagePublisher();
SalsaMessage msg = new SalsaMessage(SalsaMessage.MESSAGE_TYPE.elise_instanceInfoUpdate, ConductorConfiguration.getConductorID(), EliseQueueTopic.FEEDBACK_TOPIC, "", instances.toJson());
publish.pushMessage(msg);
}
public void sendSingleInstanceByID(UnitInstanceCollector aCollector, ServiceCategory category, String domainID) {
logger.debug("Inside the sending data method ...");
UnitInstanceWrapper wrapper = new UnitInstanceWrapper();
UnitInstance instance = aCollector.collectInstanceByID(domainID);
LocalIdentification si = aCollector.identify(instance);
GlobalIdentification gi = new GlobalIdentification(); // the instance contain a globalID, but null uuid and has 1 localID
gi.addLocalIdentification(si);
instance.setIdentification(gi);
wrapper.hasInstance(instance);
MessagePublishInterface publish = factory.getMessagePublisher();
SalsaMessage msg = new SalsaMessage(SalsaMessage.MESSAGE_TYPE.elise_instanceInfoUpdate, ConductorConfiguration.getConductorID(), EliseQueueTopic.FEEDBACK_TOPIC, "", wrapper.toJson());
publish.pushMessage(msg);
}
private static final String mainFolder = ConductorConfiguration.CURRENT_DIR + "/extensions";
private static Set<Class<? extends UnitInstanceCollector>> loadAllJar() {
logger.debug("Loading all collector in folder: {}", mainFolder);
String[] folders = listSubFolder(mainFolder);
if (folders == null) {
logger.debug("There is no collector module loaded...");
return null;
}
logger.debug("Number of child folders: " + folders.length);
List<URL> allURLs = new ArrayList<>();
for (String f : folders) {
String checkingDir = mainFolder + "/" + f;
logger.debug("Checking folder: " + f);
File file = new File(checkingDir);
String[] jars = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
});
logger.debug("Loading jar into classpath. Number of jar files: " + jars.length);
for (String jar : jars) {
String absoluteJar = checkingDir + "/" + jar;
String urlToFile = "jar:file:" + absoluteJar + "!/";
try {
logger.debug("adding url to file: " + urlToFile);
allURLs.add(new URL(urlToFile));
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
}
}
logger.debug("Using reflections to gather all UnitInstanceCollector classes");
urlClassLoader = new URLClassLoader(allURLs.toArray(new URL[allURLs.size()]));
Reflections reflextions = new Reflections(new ConfigurationBuilder()
//.addScanners(new SubTypesScanner())
.filterInputsBy(new FilterBuilder().include("at.ac.tuwien.*"))
.addClassLoader(urlClassLoader)
.setScanners(new SubTypesScanner())
.addUrls(ClasspathHelper.forClassLoader(urlClassLoader)));
Set<Class<? extends UnitInstanceCollector>> classes = reflextions.getSubTypesOf(UnitInstanceCollector.class);
logger.debug("Loaded {} Elise collector(s) in the ./extensions folder", classes.size());
for (Class c : classes) {
if (c == null) {
logger.error("Loaded class is null, I do not know what is happening !");
} else {
logger.debug(c.toString());
}
}
return classes;
}
private static String[] listSubFolder(String mainFolder) {
File file = new File(mainFolder);
String[] directories = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory();
}
});
return directories;
}
private static String executeCommand3(String command, String workingDir) {
StringBuffer output = new StringBuffer();
String[] env = {"/bin", "/usr/bin", "/opt/java/bin"};
try {
Process p = Runtime.getRuntime().exec(command, env, new File(workingDir));
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader reader1 = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = "";
while ((line = reader.readLine()) != null) {
logger.debug(line);
}
while ((line = reader1.readLine()) != null) {
logger.debug(line);
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}