content: /* 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]"
   *
   * $Id$
   *
   * Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
   */
  package com.sun.faban.harness.agent;
  import com.sun.faban.harness.common.Config;
  import com.sun.faban.harness.engine.DeployImageClassLoader;
  import com.sun.faban.harness.tools.CommandLineTool;
  import com.sun.faban.harness.tools.MasterToolContext;
  import com.sun.faban.harness.tools.ToolDescription;
  import com.sun.faban.harness.tools.ToolWrapper;
  import com.sun.faban.harness.util.XMLReader;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import java.io.File;
  import java.io.IOException;
  import java.rmi.RemoteException;
  import java.rmi.server.UnicastRemoteObject;
  import java.rmi.server.Unreferenced;
  import java.util.*;
  import java.util.concurrent.CountDownLatch;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  /**
   * This class implements the ToolAgent interface.
   * It runs in a seperate VM and is started by the GenericBenchmark.
   * It configures and manages all tools on a particular machine
   * and performs the ToolService API functions for this machine.
   * Each tool is run as a seperate thread.
   *
   * @see com.sun.faban.harness.engine.ToolService
   * @see com.sun.faban.harness.engine.GenericBenchmark
   * @author Ramesh Ramachandran
   */
  public class ToolAgentImpl extends UnicastRemoteObject implements ToolAgent, Unreferenced {
      String toolNames[];
      ToolWrapper tools [];	// handles to the Tool objects
      int numTools;
      static String masterMachine = null;	// name of master machine
      static String host;	// our current hostname
      Logger logger;
      CountDownLatch latch;
  
      /**
       * Constructor for Tool agent implementation.
       * @throws java.rmi.RemoteException Error constructing ToolAgentImpl
       */
      public ToolAgentImpl() throws RemoteException {
          super();
          logger = Logger.getLogger(this.getClass().getName());
          host = CmdAgentImpl.getHost();
          masterMachine = CmdAgentImpl.getMaster();
          logger.fine("Started processing tools");
      }
  
      /**
       * This method configures the tools that must be run on
       * this machine by calling the configure method on each of
       * the specified tools.
       * @param toolList - each element in the array is the
       * name of a tool and optional arguments, e.g "sar -u -c"
       * @param osToolSet list of os tools
       * @param outDir output directory of the run
       * @throws IOException
       *
       */
      public void configure(List<MasterToolContext> toolList,
                            Set<String> osToolSet, String outDir)
              throws IOException {
          List<MasterToolContext> toollist = new ArrayList<MasterToolContext>();
          if(toolList != null)
              toollist.addAll(toolList);
  
          LinkedHashMap<String, List<String>> toolSetsMap = parseOSToolSets();
          if (osToolSet != null) {
              for (String tool : osToolSet) {
                  StringTokenizer tt = new StringTokenizer(tool);
                  String toolId = tt.nextToken();
                  Set<String> toolsetTools = new LinkedHashSet<String>();
                  if (toolSetsMap.containsKey(toolId)) {
                      toolsetTools.addAll(toolSetsMap.get(toolId));
                      for (String t1 : toolsetTools) {
                          MasterToolContext tCtx = new MasterToolContext(
                                  t1, null, null);
                          if (tCtx != null) {
                              toollist.add(tCtx);
                          }
                      }
                  } else {
                      MasterToolContext tCtx = new MasterToolContext(
                              tool, null, null);
                      if (tCtx != null) {
                          toollist.add(tCtx);
                      }
                  }
              }
          }
          numTools = toollist.size();
          toolNames = new String[numTools];
          tools = new ToolWrapper[numTools];
  
          logger.fine("Processing tools");
          latch = new CountDownLatch(toollist.size());
          
          for (int i=0; i<toollist.size(); i++) {
              MasterToolContext ctx = toollist.get(i);
              String toolId = ctx.getToolId();
              toolNames[i] = toolId;
              //List<String> args = Command.parseArgs(ctx.getToolParams());
             
  
              String path = null;
              int nameIdx = toolId.lastIndexOf(File.separator) + 1;
              if (nameIdx > 0) {
                  path = toolId.substring(0, nameIdx - 1);
                  toolId = toolId.substring(nameIdx);
              }
  
              if (path != null && path.length() == 0)
                  path = null;
  
              String toolClass = null;
              ToolDescription toolDesc = ctx.getToolDescription();
              if (toolDesc != null)
                  toolClass = toolDesc.getToolClass();
  
              // Now, create the tool object and call its configure method            
              if (toolClass != null) {
                  try {
                      DeployImageClassLoader loader = DeployImageClassLoader.
                              getInstance(toolDesc.getLocationType(),
                              toolDesc.getLocation(), getClass().getClassLoader());
                      Class c = loader.loadClass(toolClass);
                      tools[i] = new ToolWrapper(c, ctx);
                      logger.fine("Trying to run tool " + c.getName());
                      tools[i].configure(toolNames[i], path, outDir, host, CmdAgentImpl.getHandle(), latch);
                  } catch (ClassNotFoundException ce) {
                      logger.log(Level.WARNING, "Class " + toolClass + " not found");
                      latch.countDown();
                  } catch (Exception ie) {
                      logger.log(Level.WARNING, "Error in creating tool object " +
                                 toolClass, ie);
                      latch.countDown(); // Tool did not get started.
                  }
              } else if (!"default".equals(ctx.getToolId()) || 
                         (ctx.getToolParams() != null &&
                          ctx.getToolParams().trim().length() > 0)) {
                  try {
                      tools[i] = new ToolWrapper(CommandLineTool.class, ctx);
                      tools[i].configure(toolNames[i], path, outDir, host, CmdAgentImpl.getHandle(), latch);
                      logger.fine("Trying to run tool " + tools[i] + " using CommandLineTool.");
                  } catch (Exception ex) {
                      logger.log(Level.WARNING, "Cannot start CommandLineTool!", ex);
                      latch.countDown();
                  }
              } else {
                  latch.countDown();
              }
          }
      }
  
      /**
       * This method is responsible for starting all tools.
       * @param 	delay - time to delay before starting
       * @return true if all tools started successfully, else false
       * @throws RemoteException
       */
      public boolean start(int delay) throws RemoteException {
          int i;
          boolean ret = true;
          for (i = 0; i < tools.length; i++) {
              if (tools[i] == null || ! tools[i].start(delay)) {
                  ret = false;
                  logger.severe("Could not start tool " + toolNames[i]);
              }
          }
          return(ret);
      }
  
  
      /**
       * This method is responsible for starting all tools.
       * @param 	delay - time to delay before starting
       * @param duration after which tools must be stopped
       * @return true if all tools started successfully, else false
       * @throws RemoteException
       */
      public boolean start(int delay, int duration) throws RemoteException {
          int i;
          boolean ret = true;
          for (i = 0; i < tools.length; i++) {
              if (tools[i] == null || ! tools[i].start(delay, duration)) {
                  ret = false;
                  logger.severe("Could not start tool " + toolNames[i]);
              }
          }
          return(ret);
      }
  
      /**
       * Start only specified tools.
       * @param 	delay - time to delay before starting
       * @param 	t - specific list of tools to start
       * @return  true if all tools are started successfully, false otherwise
       * @throws RemoteException A communication error occurred
       */
      public boolean start(int delay, String[] t)
              throws RemoteException {
          int i, j;
          boolean ret = true;
          for (j = 0; j < t.length; j++) {
              for (i = 0; i < tools.length; i++) {
                  if (toolNames[i].equals(t[j])) {
                      if ( tools[i] == null || ! tools[i].start(delay)) {
                          ret = false;
                          logger.severe("Could not start tool " +
                                      toolNames[i]);
                      }
                      break;
                  }
              }
          }
          return(ret);
      }
  
      /**
       * Start only specified tools for specific duration.
       * @param delay - time to delay before starting
       * @param t - specific list of tools to start
       * @param duration after which tools must be stopped
       * @return true if all tools are started successfully, false otherwise
       * @throws RemoteException A communication error occurred
       */
      public boolean start(int delay, String[] t, int duration)
              throws RemoteException {
          int i, j;
          boolean ret = true;
          for (j = 0; j < t.length; j++) {
              for (i = 0; i < tools.length; i++) {
                  if (toolNames[i].equals(t[j])) {
                      if ( tools[i] == null || ! tools[i].start(delay, duration)) {
                          ret = false;
                          logger.severe("Could not start tool " +
                                  toolNames[i]);
                      }
                      break;
                  }
              }
          }
          return(ret);
      }
  
      /**
       * This method is responsible for stopping the tools.
       */
      public void stop() {
          for (int i = 0; i < tools.length; i++) {
              if (tools[i] != null){
                  try {
                      tools[i].stop();
                  }
                  catch (Exception e) {
                      logger.log(Level.WARNING, "ToolAgent: toolName = " +
                              toolNames[i] + " cannot stop", e);
                  }
              }
          }
      }
  
      /**
       * Stopping specific tools.
       * @param t The tools to stop.
       */ 
      public void stop(String t[]) {
          for (String tool : t) {
              for (int i = 0; i < tools.length; i++) {
                  if (tools[i] != null && toolNames[i].equals(tool)) {
                      try {
                          tools[i].stop();
                      } catch (Exception ex) {
                          logger.log(Level.SEVERE, null, ex);
                      }
                      break;
                  }
              }
          }
      }
  
      /**
       * This method is responsible for post processing tools.
       */
      public void postprocess() {
          for (int i = 0; i < tools.length; i++) {
              if (tools[i] != null){
                  try {
                      logger.finer("Postprocessing " + toolNames[i]);
                      tools[i].postprocess();
                  }
                  catch (Exception e) {
                      logger.log(Level.WARNING, "ToolAgent: toolName = " +
                              toolNames[i] + " cannot stop", e);
                  }
              }
          }
      }
  
      /**
       *  Waits for all tools to finish up.
       */
      public void waitFor() {
          try {
              latch.await();
          } catch (InterruptedException e) {
              logger.log(Level.WARNING,
                      "Interrupted waiting for tools to finish.", e);
          }
      }
  
      /**
       *  Kills all the tools.
       */
      public void kill() {
          logger.info("Killing tools");
          for (int i = 0; i < tools.length; i++){
              if (tools[i] != null){
                  try {
                      tools[i].kill();
                  } catch (Exception e) {
                      logger.log(Level.WARNING, "ToolAgent: toolName = " +
                              toolNames[i] + " cannot kill", e);
                  }
              }
          }
              
      }
  
      /**
       * When this instance is unreferenced the application must exit.
       *
       * @see         java.rmi.server.Unreferenced
       *
       */
      public void unreferenced() {
          kill();
      } 
  
      /**
       * Obtains the OS toolsets.
       * @return LinkedHashMap
       */
      protected LinkedHashMap<String, List<String>> parseOSToolSets() {
          LinkedHashMap<String, List<String>> toolSetsMap =
              new LinkedHashMap<String, List<String>>();
          File toolsetsXml = new File(Config.CONFIG_DIR + Config.OS_DIR + "toolsets.xml");
          try {
              if(toolsetsXml.exists()){
                  XMLReader reader = new XMLReader(Config.CONFIG_DIR + Config.OS_DIR + "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 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));
                          }
                          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(name) &&
                                  (toolIncludes != null || base != null)) {
                              if (toolSetsMap.containsKey(name)) {
                                  logger.log(Level.WARNING,
                                          "Ignoring duplicate toolset = " + name);
                              } else {
                                  toolSetsMap.put(name, toolsCmds);
                              }
                          }
                      }
                  }
              }
          } catch  (Exception e) {
              logger.log(Level.WARNING, "Error reading toolsets.xml ", e);
          }
  
          return toolSetsMap;
      }
  }