package org.sergilos.servicemanager;
import com.google.common.collect.ImmutableSet;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;
public class ServiceServerManager implements ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceServerManager.class);
public final static String CONFIG_SEPARATOR = ",";
private Map<String, AbstractRunnableServiceWrapper> serviceMap = new HashMap<String, AbstractRunnableServiceWrapper>();
private Map<String, Thread> threadServiceMap = new HashMap<String, Thread>();
private ApplicationContext applicationContext;
private AbstractRunnableServiceWrapper.ServiceWrapperFactory serviceWrapperFactory;
private List<String> serviceNamesList;
private List<String> serviceInterfacesList;
private List<String> serviceImplementationsList;
private List<String> servicePortsList;
private boolean startServices = true;
public ServiceServerManager(String xmlConfigurationLocation, AbstractRunnableServiceWrapper.ServiceWrapperFactory serviceWrapperFactory) {
this.serviceNamesList = new ArrayList<>();
this.serviceInterfacesList = new ArrayList<>();
this.serviceImplementationsList = new ArrayList<>();
this.servicePortsList = new ArrayList<>();
this.startServices = true;
this.serviceWrapperFactory = serviceWrapperFactory;
try {
initializeByXml(xmlConfigurationLocation);
} catch (XPathExpressionException | ParserConfigurationException | SAXException | IOException e) {
throw new IllegalArgumentException("Error loading configuration from xml " + xmlConfigurationLocation, e);
}
}
private void initializeByXml(String xmlConfigurationLocation) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
LOGGER.info("Initializing by xml: " + xmlConfigurationLocation);
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
InputStream inputStream = new FileInputStream(xmlConfigurationLocation);
Document document = builder.parse(inputStream);
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList serviceNodes = (NodeList) xPath.compile("/thirftServiceConfiguration/service").evaluate(document, XPathConstants.NODESET);
for (int i = 0; serviceNodes != null && i < serviceNodes.getLength(); i++) {
Node serviceNode = serviceNodes.item(i);
if (serviceNode.getNodeType() == Node.ELEMENT_NODE) {
NamedNodeMap serviceAttributes = serviceNode.getAttributes();
NodeList serviceDefinitionNodes = serviceNode.getChildNodes();
for (int x = 0; serviceDefinitionNodes != null && x < serviceDefinitionNodes.getLength(); x++) {
Node serviceDefinitionNode = serviceDefinitionNodes.item(x);
if (serviceDefinitionNode.getNodeType() == Node.ELEMENT_NODE) {
NamedNodeMap serviceDefinitionAttributes = serviceDefinitionNode.getAttributes();
String serviceName = serviceAttributes.getNamedItem("name").getNodeValue();
String servicePort = serviceAttributes.getNamedItem("port").getNodeValue();
String serviceInterface = serviceDefinitionAttributes.getNamedItem("interface").getNodeValue();
String serviceImplementation = serviceDefinitionAttributes.getNamedItem("implementation").getNodeValue();
this.serviceNamesList.add(serviceName);
this.servicePortsList.add(servicePort);
this.serviceInterfacesList.add(serviceInterface);
this.serviceImplementationsList.add(serviceImplementation);
LOGGER.debug("Service entry: {}:{} [ {}->{} ]", serviceName, servicePort, serviceInterface, serviceImplementation);
}
}
}
}
}
public ServiceServerManager(String serviceNamesString, String serviceInterfacesString, String serviceImplementationsString, String servicePortsString,
AbstractRunnableServiceWrapper.ServiceWrapperFactory serviceWrapperFactory) {
if (serviceNamesString == null || serviceInterfacesString == null || serviceImplementationsString == null || servicePortsString == null) {
throw new IllegalArgumentException("One or more parameters are null, but all the ServiceManager parameters are mandatory");
}
String[] serviceNames = serviceNamesString.split(CONFIG_SEPARATOR);
String[] serviceInterfaces = serviceInterfacesString.split(CONFIG_SEPARATOR);
String[] serviceImplementations = serviceImplementationsString.split(CONFIG_SEPARATOR);
String[] servicePorts = servicePortsString.split(CONFIG_SEPARATOR);
this.serviceNamesList = Arrays.asList(serviceNames);
this.serviceInterfacesList = Arrays.asList(serviceInterfaces);
this.serviceImplementationsList = Arrays.asList(serviceImplementations);
this.servicePortsList = Arrays.asList(servicePorts);
this.serviceWrapperFactory = serviceWrapperFactory;
}
public ServiceServerManager(String serviceNamesString, String serviceInterfacesString, String serviceImplementationsString, String servicePortsString,
boolean startServices, AbstractRunnableServiceWrapper.ServiceWrapperFactory serviceWrapperFactory) {
this(serviceNamesString, serviceInterfacesString, serviceImplementationsString, servicePortsString, serviceWrapperFactory);
this.startServices = startServices;
this.serviceWrapperFactory = serviceWrapperFactory;
}
public ServiceServerManager(List<String> serviceNamesList, List<String> serviceInterfacesList, List<String> serviceImplementationsList,
List<String> servicePortsList, AbstractRunnableServiceWrapper.ServiceWrapperFactory serviceWrapperFactory) {
if (serviceNamesList == null || serviceImplementationsList == null || serviceImplementationsList == null || servicePortsList == null) {
throw new IllegalArgumentException("One or more parameters are null, but all the ServiceManager parameters are mandatory");
}
this.serviceNamesList = serviceNamesList;
this.serviceInterfacesList = serviceInterfacesList;
this.serviceImplementationsList = serviceImplementationsList;
this.servicePortsList = servicePortsList;
this.serviceWrapperFactory = serviceWrapperFactory;
}
public void startupServer() throws TTransportException {
LOGGER.debug("Startup ServiceManager Server");
initializeServiceMap(serviceNamesList, serviceInterfacesList, serviceImplementationsList, servicePortsList);
threadServiceMap.clear();
for (Entry<String, AbstractRunnableServiceWrapper> serviceEntry : serviceMap.entrySet()) {
Thread serviceThread = new Thread(serviceEntry.getValue());
threadServiceMap.put(serviceEntry.getKey(), serviceThread);
if (this.startServices) {
LOGGER.debug("Initializing created Thrift services");
serviceThread.start();
}
}
}
public void stopServices() {
LOGGER.debug("Stopping ServiceManager Server");
for (Entry<String, AbstractRunnableServiceWrapper> serviceEntry : serviceMap.entrySet()) {
serviceEntry.getValue().stopService();
}
}
public Collection<AbstractRunnableServiceWrapper> getSerivesList() {
return ImmutableSet.<AbstractRunnableServiceWrapper> builder().addAll(serviceMap.values()).build();
}
public AbstractRunnableServiceWrapper getService(String serviceName) {
return serviceMap.get(serviceName);
}
private void initializeServiceMap(List<String> serviceNamesList, List<String> serviceInterfacesList, List<String> serviceImplementationsList,
List<String> servicePortsList) throws TTransportException {
if (serviceInterfacesList.isEmpty() || serviceInterfacesList.size() != serviceImplementationsList.size()
|| serviceInterfacesList.size() != servicePortsList.size() || serviceInterfacesList.size() != serviceNamesList.size()) {
throw new IllegalArgumentException(
String.format(
"Invalid service definition. Expecting four not empty lists of the same size, but got: Names: %s \nInterfaces: %s \nImplementations: %s \nPorts: %s",
serviceNamesList, serviceInterfacesList, serviceImplementationsList, servicePortsList));
}
for (int i = 0; i < serviceInterfacesList.size(); i++) {
String serviceName = serviceNamesList.get(i).trim();
AbstractRunnableServiceWrapper serviceWrapper = serviceMap.get(serviceName);
if (serviceWrapper == null) {
serviceWrapper = serviceWrapperFactory.getServiceServerWrapper(applicationContext, serviceName, new Integer(servicePortsList.get(i).trim()));
serviceMap.put(serviceName, serviceWrapper);
LOGGER.debug("Initializing service: " + serviceName);
}
serviceWrapper.addProcessor(serviceImplementationsList.get(i).trim(), serviceInterfacesList.get(i).trim());
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}