/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.sun.com/cddl/cddl.html or
* install_dir/legal/LICENSE
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at install_dir/legal/LICENSE.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.harness.services;
import com.sun.faban.common.NameValuePair;
import com.sun.faban.common.ParamReader;
import com.sun.faban.harness.ConfigurationException;
import com.sun.faban.harness.ParamRepository;
import com.sun.faban.harness.common.Config;
import com.sun.faban.harness.common.Run;
import com.sun.faban.harness.engine.CmdService;
import com.sun.faban.harness.engine.DeployImageClassLoader;
import com.sun.faban.harness.tools.MasterToolContext;
import com.sun.faban.harness.tools.ToolDescription;
import com.sun.faban.harness.util.XMLReader;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class manages all the services.
*
* @author Sheetal Patil
*/
public class ServiceManager {
private static Logger logger =
Logger.getLogger(ServiceContext.class.getName());
private static ServiceManager instance = null;
private List<ServiceWrapper> loadedServicesList =
new ArrayList<ServiceWrapper>();
private List<ServiceContext> ctxList = new ArrayList<ServiceContext>();
private LinkedHashMap<String, ServiceDescription> serviceMap =
new LinkedHashMap<String, ServiceDescription>();
private LinkedHashMap<String, ToolDescription> toolMap =
new LinkedHashMap<String, ToolDescription>();
private LinkedHashMap<String, List<String>> toolSetsMap =
new LinkedHashMap<String, List<String>>();
ArrayList<MasterToolContext> toolList = new ArrayList<MasterToolContext>();
HashSet<String> activeDeployments = new HashSet<String>();
ArrayList<NameValuePair<String>> hostDeploymentList =
new ArrayList<NameValuePair<String>>();
private Run run;
/**
* Obtains the set of active services and tools deployments used in the
* current run.
* @return The set of active deployments or null if there is no current run
*/
public static Set<String> getActiveDeployments() {
Set<String> deployments = null;
if (instance != null) {
deployments = instance.activeDeployments;
}
return deployments;
}
/**
* Constructor.
* @param par The param repository
* @param run The run object
* @throws java.lang.Exception If there is an error constructing
*/
public ServiceManager(ParamRepository par, Run run)
throws Exception{
String benchmark = run.getBenchmarkName();
this.run = run;
// Get the service descriptions and tool descriptions
parseAvailableServices(benchmark);
// Obtain the active service list.
parseRequestedServicesAndTools(par);
initiateDownload();
for (ServiceContext ctx : ctxList) {
DeployImageClassLoader loader = DeployImageClassLoader.getInstance(
ctx.desc.locationType, ctx.desc.location,
getClass().getClassLoader());
ServiceWrapper wrapper = new ServiceWrapper(
loader.loadClass(ctx.desc.serviceClass), ctx);
loadedServicesList.add(wrapper);
}
this.loadedServicesList = Collections.unmodifiableList(
loadedServicesList);
this.ctxList = Collections.unmodifiableList(ctxList);
instance = this;
}
private void parseAvailableServices(String benchmark) {
parseServicesAndTools("benchmarks", benchmark);
parseToolSets("benchmarks", benchmark);
File serviceDir = new File(Config.SERVICE_DIR);
File[] serviceBundles;
if (serviceDir.isDirectory()) {
serviceBundles = serviceDir.listFiles();
for (File serviceBundle : serviceBundles) {
if (!serviceBundle.isDirectory())
continue;
parseServicesAndTools("services", serviceBundle.getName());
parseToolSets("services", serviceBundle.getName());
}
bindServices();
}
}
/**
* Parses a service/tool bundle.
* @param type The location type, services or benchmark
* @param dir The deploy jar name, without .jar
*/
public void parseServicesAndTools(String type, String dir) {
String metaInf = Config.FABAN_HOME + File.separator + type +
File.separator + dir + File.separator + "META-INF";
File metaInfDir = new File(metaInf);
if (!metaInfDir.isDirectory()) {
return;
}
File serviceXml = new File(metaInf, "services-tools.xml");
try {
if (serviceXml.exists()) {
XMLReader reader = new XMLReader(metaInf + File.separator +
"services-tools.xml");
Element root;
if (reader != null) {
root = reader.getRootNode();
// First, parse the services.
NodeList serviceNodes = reader.getNodes("service", root);
for (int i = 0; i < serviceNodes.getLength(); i++) {
Node serviceNode = serviceNodes.item(i);
if (serviceNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
Element se = (Element) serviceNode;
String id = null;
NamedNodeMap attrList = serviceNode.getAttributes();
for (int j = 0; j < attrList.getLength(); j++) {
if (attrList.item(j).getNodeType() ==
Node.ATTRIBUTE_NODE) {
if (attrList.item(j).getNodeName().equals("id")) {
id = attrList.item(j).getNodeValue();
}
}
}
NodeList classNodes = serviceNode.getChildNodes();
String loadableClass = null;
for (int k = 0; k < classNodes.getLength(); k++) {
if (classNodes.item(k).getNodeType() ==
Node.ELEMENT_NODE) {
loadableClass = reader.getValue("class", se);
}
}
if (id != null && loadableClass != null) {
if (serviceMap.containsKey(id)) {
logger.log(Level.WARNING,
"Ignoring duplicate service " + id +
" in " + type + File.separator + dir);
} else {
ServiceDescription desc =
new ServiceDescription(id,
loadableClass, type, dir);
serviceMap.put(id, desc);
}
}
}
// Then parse the tools.
NodeList toolNodes = reader.getNodes("tool", root);
for (int i = 0; i < toolNodes.getLength(); i++) {
String id = null;
String serviceName = null;
String toolClass = null;
Node toolNode = toolNodes.item(i);
if (toolNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
Element te = (Element) toolNode;
NamedNodeMap attrList = toolNode.getAttributes();
for (int j = 0; j < attrList.getLength(); j++) {
if (attrList.item(j).getNodeType() ==
Node.ATTRIBUTE_NODE) {
if (attrList.item(j).getNodeName().equals("id")) {
id = attrList.item(j).getNodeValue();
}
if (attrList.item(j).getNodeName().
equals("service")) {
serviceName = attrList.item(j).getNodeValue();
}
}
}
NodeList classNodes = toolNode.getChildNodes();
for (int k = 0; k < classNodes.getLength(); k++) {
if (classNodes.item(k).getNodeType() ==
Node.ELEMENT_NODE) {
toolClass = reader.getValue("class", te);
}
}
if (id != null) {
String key = id;
if (serviceName != null) {
key += '/' + serviceName;
}
if (toolMap.containsKey(key)) {
logger.log(Level.WARNING,
"Ignoring duplicate tool" + id);
} else {
toolMap.put(key, new ToolDescription(id,
serviceName, toolClass, type, dir));
}
}
}
}
}
} catch (Exception e) {
logger.log(Level.WARNING, "Error reading benchmark " +
"descriptor for " + dir, e);
}
}
private void bindServices() {
for (Map.Entry<String, ToolDescription> entry : toolMap.entrySet()) {
ToolDescription toolDesc = entry.getValue();
if (!toolDesc.bind(serviceMap))
logger.warning("Tool " + toolDesc.getId() + " at " +
toolDesc.getLocationType() + File.separator +
toolDesc.getLocation() +
" references non-existent service " +
toolDesc.getServiceName());
}
}
private void parseRequestedServicesAndTools(ParamRepository par)
throws IOException, ConfigurationException {
HashSet<NameValuePair<String>> hostDeploymentSet =
new HashSet<NameValuePair<String>>();
NodeList topLevelElements = par.getTopLevelElements();
int topLevelSize = topLevelElements.getLength();
Properties properties = null;
for (int i = 0; i < topLevelSize; i++) {
Node node = topLevelElements.item(i);
if (node.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
Element ti = (Element) node;
String ns = ti.getNamespaceURI();
String topElement = ti.getNodeName();
if (ParamReader.FABANURI.equals(ns) && "fa:runConfig".
equals(topElement))
continue;
if("http://faban.sunsource.net/ns/fabanharness".equals(ns) &&
"jvmConfig".equals(topElement))
continue;
// Get the hosts
String[] hosts = par.getEnabledHosts(ti);
if (hosts == null || hosts.length == 0)
continue;
// Get the services
NodeList serviceNodes = par.getNodes("fh:service", ti);
int serviceCount = serviceNodes.getLength();
for (int j = 0; j < serviceCount; j++) {
Element serviceElement = (Element) serviceNodes.item(j);
String serviceName = par.getParameter("fh:name",
serviceElement);
//boolean enabled = par.getBooleanValue("fh:enabled", true);
boolean enabled = par.getBooleanValue("fh:enabled", serviceElement);
if (!enabled)
continue;
boolean restart = par.getBooleanValue("fh:restart",
serviceElement);
Node configNode = par.getNode("fh:config", serviceElement);
if(configNode != null) {
properties = new Properties();
NodeList propsList = configNode.getChildNodes();
for (int k = 0; k < propsList.getLength(); k++) {
if (propsList.item(k).getNodeType() ==
Node.ELEMENT_NODE) {
Element propElement = (Element) propsList.item(k);
String key = propsList.item(k).getNodeName();
NodeList props = propElement.getChildNodes();
int propValueLength = props.getLength();
for (int l = 0; l < propValueLength; l++) {
Node valueNode = props.item(l);
if (valueNode.getNodeType() == Node.TEXT_NODE) {
String value =
valueNode.getNodeValue().trim();
properties.setProperty(key, value);
break;
}
}
}
}
}
ServiceDescription sd = serviceMap.get(serviceName);
if (sd == null) {
logger.warning("Cannot find service: " + serviceName +
". Service may not be deployed.");
continue;
}
// Benchmarks get loaded automatically and have their checks.
// We need to mark a service only in the services directory.
if ("services".equals(sd.locationType)) {
File runIdFile = new File(Config.SERVICE_DIR +
sd.location + File.separator +
"META-INF" + File.separator + "RunID");
FileWriter wFile = new FileWriter(runIdFile);
wFile.write(run.getRunId());
wFile.close();
}
ServiceContext ctx =
new ServiceContext(sd, par, ti, properties, restart);
ctxList.add(ctx);
if ("services".equals(sd.locationType)) {
String fullLocation = sd.locationType + '/' + sd.location;
activeDeployments.add(fullLocation);
CmdService cmds = CmdService.getHandle();
String[] ctxHosts = ctx.getHosts();
for (String ctxHost : ctxHosts) {
if (hostDeploymentSet.add(new NameValuePair<String>(
cmds.getHostName(ctxHost), sd.location))) {
hostDeploymentList.add(new NameValuePair<String>(
ctxHost, sd.location));
}
}
}
String toolCmds = par.getParameter("fh:tools", serviceElement);
Set<String> tools = new LinkedHashSet<String>();
if (toolCmds.toUpperCase().equals("NONE")) {
} else if (toolCmds.length() != 0){
StringTokenizer st = new StringTokenizer(toolCmds, ";");
while (st.hasMoreTokens()) {
tools.add(st.nextToken().trim());
}
} else {
String key = "default" + '/' + serviceName;
Set<String> toolsetTools = new LinkedHashSet<String>();
if(toolSetsMap.containsKey(key)){
toolsetTools.addAll(toolSetsMap.get(key));
for (String t1 : toolsetTools) {
StringTokenizer tt1 = new StringTokenizer(t1);
String toolId1 = tt1.nextToken();
String toolKey1 = toolId1 + '/' + serviceName;
ToolDescription toolDesc = toolMap.get(toolKey1);
MasterToolContext toolCtx;
if (toolDesc != null) {
toolCtx = new MasterToolContext(t1, ctx,
toolDesc);
} else {
toolDesc = toolMap.get(toolId1);
if (toolDesc != null) {
toolCtx = new MasterToolContext(
t1, ctx, toolDesc);
activeDeployments.add(
toolDesc.getLocationType() +
File.separator +
toolDesc.getLocation());
} else {
//logger.info("No Tool Description for tool: " + t1);
logger.fine("No Tool Description for tool: "
+ t1 +" ,so it's a command line tool");
toolCtx = new MasterToolContext(
t1, ctx, null);
}
}
if (toolCtx != null) {
toolList.add(toolCtx);
}
}
}
}
// Analyze the list of tools for toolset references.
for (String tool : tools) {
StringTokenizer tt = new StringTokenizer(tool);
String toolId = tt.nextToken();
String toolKey = toolId + '/' + serviceName;
Set<String> toolsetTools = new LinkedHashSet<String>();
if (toolSetsMap.containsKey(toolKey)) {
toolsetTools.addAll(toolSetsMap.get(toolKey));
for (String t1 : toolsetTools) {
StringTokenizer tt1 = new StringTokenizer(t1);
String toolId1 = tt1.nextToken();
String toolKey1 = toolId1 + '/' + serviceName;
ToolDescription toolDesc = toolMap.get(toolKey1);
MasterToolContext toolCtx;
if (toolDesc != null) {
toolCtx = new MasterToolContext(t1, ctx, toolDesc);
} else {
toolDesc = toolMap.get(toolId1);
if (toolDesc != null) {
toolCtx = new MasterToolContext(
t1, ctx, toolDesc);
} else {
logger.fine("No Tool Description for tool: "
+ t1 +
" ,so it's a command line tool");
toolCtx = new MasterToolContext(
t1, ctx, null);
}
}
if (toolCtx != null) {
toolList.add(toolCtx);
}
}
} else {
ToolDescription toolDesc = toolMap.get(toolKey);
MasterToolContext toolCtx;
if (toolDesc != null) {
toolCtx = new MasterToolContext(tool, ctx, toolDesc);
} else {
toolDesc = toolMap.get(toolId);
if (toolDesc != null) {
toolCtx = new MasterToolContext(
tool, ctx, toolDesc);
} else {
logger.fine("No Tool Description for tool: " +
tool + " ,so it's a command line tool");
toolCtx = new MasterToolContext(
tool, ctx, null);
}
}
if (toolCtx != null) {
toolList.add(toolCtx);
}
}
}
for (MasterToolContext toolCtx : toolList) {
String locationType;
if (toolCtx.getToolDescription() != null) {
locationType =
toolCtx.getToolDescription().getLocationType();
if ("services".equals(locationType)) {
String location =
toolCtx.getToolDescription().getLocation();
String fullLocation = locationType + '/' + location;
activeDeployments.add(fullLocation);
CmdService cmds = CmdService.getHandle();
String[] ctxHosts =
toolCtx.getToolServiceContext().getHosts();
for (String ctxHost : ctxHosts) {
if (hostDeploymentSet.add(new NameValuePair<String>(
cmds.getHostName(ctxHost), location))) {
hostDeploymentList.add(new NameValuePair<String>
(ctxHost, location));
}
}
}
}
}
}
}
}
/**
* Parses and creates the appropriate structures for the tool sets.
* @param type The location type, services or benchmark
* @param dir The deploy jar name, without .jar
*/
public void parseToolSets(String type, String dir) {
String metaInf = Config.FABAN_HOME + File.separator + type +
File.separator + dir + File.separator + "META-INF";
File metaInfDir = new File(metaInf);
if (!metaInfDir.isDirectory()) {
return;
}
File toolsetsXml = new File(metaInf + File.separator +
"toolsets.xml");
try {
if(toolsetsXml.exists()){
XMLReader reader = new XMLReader(metaInf + File.separator +
"toolsets.xml");
Element root;
if (reader != null) {
root = reader.getRootNode();
// First, parse the services.
NodeList toolsetsNodes = reader.getNodes("toolset", root);
for (int i = 0; i < toolsetsNodes.getLength(); i++) {
Node toolsetsNode = toolsetsNodes.item(i);
if (toolsetsNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
Element tse = (Element) toolsetsNode;
ArrayList<String> toolsCmds = new ArrayList<String>();
String service = reader.getValue("service", tse);
String name = reader.getValue("name", tse);
String base = reader.getValue("base", tse);
List<String> toolIncludes = reader.getValues("includes", tse);
List<String> toolExcludes = reader.getValues("excludes", tse);
if(!"".equals(base)){
toolsCmds.addAll(toolSetsMap.get(base+"/"+service));
}
if(toolIncludes != null){
for (String tool : toolIncludes){
StringTokenizer st = new StringTokenizer(tool, ";");
while(st.hasMoreTokens())
toolsCmds.add(st.nextToken().trim());
}
}
if(toolExcludes != null){
ArrayList<String> td = new ArrayList<String>();
for (String tool : toolExcludes){
StringTokenizer st = new StringTokenizer(tool, ";");
while(st.hasMoreTokens())
td.add(st.nextToken().trim());
}
toolsCmds.removeAll(td);
}
if (!"".equals(service) && !"".equals(name) &&
(toolIncludes != null || base != null)) {
String key = name + "/" + service;
if (toolSetsMap.containsKey(key)) {
logger.log(Level.WARNING,
"Ignoring duplicate toolset = " + key);
} else {
toolSetsMap.put(key, toolsCmds);
}
}
}
}
}
} catch (Exception e) {
logger.log(Level.WARNING, "Error reading toolsets.xml " +
"for " + dir, e);
}
}
private void initiateDownload() throws RemoteException {
HashMap<String, List<String>> downloadMap = new HashMap<String, List<String>>();
for (NameValuePair<String> hostPath : hostDeploymentList) {
List<String> pathList = downloadMap.get(hostPath.name);
if (pathList == null) {
pathList = new ArrayList<String>();
downloadMap.put(hostPath.name, pathList);
}
pathList.add(hostPath.value);
}
Set<Map.Entry<String, List<String>>> entrySet = downloadMap.entrySet();
CmdService cmds = CmdService.getHandle();
for (Map.Entry<String, List<String>> entry : entrySet) {
cmds.downloadServices(entry.getKey(), entry.getValue());
}
// Also update the paths on the local command agent.
ArrayList<String> activeServiceList = new ArrayList<String>();
for (String deployment : activeDeployments) {
if (deployment.startsWith("services/"))
activeServiceList.add(deployment.substring(9));
}
cmds.updatePaths(activeServiceList);
}
/**
* Returns a list of MasterToolContext.
* @return List
*/
public List<MasterToolContext> getTools() {
return toolList;
}
/**
* Configures the service.
*/
public void configure() {
for(ServiceWrapper sw : loadedServicesList)
sw.configure();
}
/**
* Obtains the configuration of a service.
*/
public void getConfig() {
for(ServiceWrapper sw : loadedServicesList)
sw.getConfig();
}
/**
* Obtains the logs of a service.
*/
public void getLogs() {
for(ServiceWrapper sw : loadedServicesList)
sw.getLogs();
}
/**
* Starts a service. If the service is marked for restart,
* this will shutdown the service and restart it.
*/
public void startup() {
// Use two separate loops to leave some time
// between shutdown and startup.
for(ServiceWrapper sw : loadedServicesList) {
if (sw.ctx.restart)
sw.shutdown();
sw.clearLogs();
}
for(ServiceWrapper sw : loadedServicesList) {
if (sw.ctx.restart)
sw.startup();
}
}
/**
* Stops a service.
*/
public void shutdown() {
for (ServiceContext ctx : ctxList) {
if ("services".equals(ctx.desc.locationType)) {
File runIdFile = new File(Config.SERVICE_DIR +
ctx.desc.location + File.separator +
"META-INF" + File.separator + "RunID");
if (runIdFile.exists())
runIdFile.delete();
}
}
for(ServiceWrapper sw : loadedServicesList){
if (sw.ctx.restart)
sw.shutdown();
}
instance = null;
}
}