/* 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.tools;
import com.sun.faban.common.CommandHandle;
import com.sun.faban.common.FileTransfer;
import com.sun.faban.harness.Configure;
import com.sun.faban.harness.Context;
import com.sun.faban.harness.Start;
import com.sun.faban.harness.Stop;
import com.sun.faban.harness.agent.CmdAgentImpl;
import com.sun.faban.harness.agent.FileAgent;
import com.sun.faban.harness.common.Config;
import com.sun.faban.harness.util.Invoker;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class is a wrapper for tool.
*
* @author Sheetal Patil
*/
public class ToolWrapper {
private static Logger logger =
Logger.getLogger(ToolWrapper.class.getName());
Object tool;
Method startMethod;
Method stopMethod;
Method configureMethod;
Method postprocessMethod;
static final int NOT_STARTED = 0;
static final int STARTED = 1;
static final int STOPPED = 2;
int toolStatus = NOT_STARTED;
CountDownLatch latch;
boolean countedDown = false;
boolean postprocessed = false;
String outfile; // Name of stdout,stderr from tool
CommandHandle outputHandle;
int outputStream;
String toolName;
String path = null; // The path to the tool.
CmdAgentImpl cmdAgent;
/** The timer used for scheduling the tools. */
protected Timer timer;
ToolContext tc = null;
private String outDir;
private String host;
/**
* Constructor.
* @param toolClass The tool class
* @param ctx The master tool context
* @throws Exception Error creating the tool
*/
public ToolWrapper(Class toolClass, MasterToolContext ctx)
throws Exception {
Method[] methods = toolClass.getMethods();
for (Method method : methods) {
// Check annotation.
if (method.getAnnotation(Start.class) != null) {
if (!Invoker.isVoidNoArg(method))
continue;
if (startMethod == null) {
startMethod = method;
} else {
logger.severe(toolName +
" Error: Multiple @Start methods.");
}
}
if (method.getAnnotation(Stop.class) != null) {
if (!Invoker.isVoidNoArg(method))
continue;
if (stopMethod == null) {
stopMethod = method;
} else {
logger.severe(toolName + " Error: Multiple @Stop methods.");
}
}
if (method.getAnnotation(Configure.class) != null) {
if (!Invoker.isVoidNoArg(method))
continue;
if (configureMethod == null) {
configureMethod = method;
} else {
logger.severe(toolName +
" Error: Multiple @Configure methods.");
}
}
if (method.getAnnotation(Postprocess.class) != null) {
if (!Invoker.isVoidNoArg(method))
continue;
if (postprocessMethod == null) {
postprocessMethod = method;
} else {
logger.severe(toolName +
" Error: Multiple @PostProcess methods.");
}
}
}
Field ctxField = null;
tc = new ToolContext(ctx.getTool(), ctx.getToolServiceContext(),
ctx.getToolDescription(), this);
Field[] fields = toolClass.getFields();
for (Field field : fields) {
if (field.getType().equals(ToolContext.class) &&
(field.getAnnotation(Context.class) != null)) {
if (ctxField == null)
ctxField = field;
else
logger.warning(toolName +
": More than one valid @Context annotation.");
}
}
Invoker.setContextLocation(tc.toolPath);
try {
tool = toolClass.newInstance();
if (ctxField != null)
ctxField.set(tool, tc);
} catch (InstantiationException e) {
Invoker.throwCauseException(e);
} finally {
Invoker.setContextLocation(null);
}
}
private void configure() throws Exception {
Invoker.invoke(tool, configureMethod, tc.toolPath);
logger.fine("Configured tool " + toolName);
}
/**
* This method is responsible for post-processing.
* @throws Exception Any exception thrown by the wrapped method
*/
public void postprocess() throws Exception {
if (postprocessed)
return;
postprocessed = true;
try {
if (toolStatus == STOPPED) {
if (postprocessMethod != null) {
logger.fine(toolName + " post-processing.");
Invoker.invoke(tool, postprocessMethod, tc.toolPath);
logger.fine("Postprocessed tool " + toolName);
}
// xfer log file to master machine, log any errors
xferLog();
logger.fine("Transfered logs for tool " + toolName);
logger.info(toolName + " Done ");
}
} finally {
finish();
}
}
/**
* This method is responsible for starting a tool.
* @throws Exception Any exception thrown by the wrapped method
*/
private void start() throws Exception {
Invoker.invoke(tool, startMethod, tc.toolPath);
toolStatus = STARTED;
logger.info("Started tool " + toolName);
}
/**
* This method is responsible for configuring a tool.
* @param toolName The name of the tool.
* @param path The path to start the command
* @param outDir The tool output directory
* @param host The host the tool should run
* @param cmdAgent The local command agent
* @param latch The latch used for identifying tool completion
* @throws java.lang.Exception If there is any error
*/
public void configure(String toolName, String path, String outDir,
String host, CmdAgentImpl cmdAgent,
CountDownLatch latch)
throws Exception {
// Prepare the context based on the params.
this.toolName = toolName;
this.cmdAgent = cmdAgent;
this.timer = cmdAgent.getTimer();
this.latch = latch;
if (path != null)
this.path = path;
this.outDir = outDir;
this.host = host;
// Get output logfile name
this.outfile = outDir + toolName + ".log." + host;
configure();
}
/**
* This method is responsible for starting a tool with given delay.
* @param delay The lapse time after which the tool will start
* @return always return true
*/
public boolean start(int delay) {
TimerTask startTask = new TimerTask() {
public void run() {
try {
start();
} catch (Exception ex) {
logger.log(Level.SEVERE, toolName + ": " + ex.getMessage(),
ex);
}
}
};
timer.schedule(startTask, delay * 1000);
return true;
}
/**
* This method is responsible for starting a tool with given delay and
* duration.
* @param delay The lapse time after which the tool will start
* @param duration The duration the tool is to run
* @return always return true
*/
public boolean start(int delay, int duration) {
if(this.start(delay)) {
timer.schedule(new StopTask(), (delay + duration) * 1000);
return true;
} else {
return false;
}
}
private class StopTask extends TimerTask {
public void run() {
try {
stop();
// Schedule it out 0.5 secs just to ensure
// the other tools' stops are called first.
timer.schedule(new PostprocessTask(), 500);
} catch (Exception ex) {
logger.log(Level.SEVERE, toolName + ": " + ex.getMessage(), ex);
}
}
}
private class PostprocessTask extends TimerTask {
public void run() {
try {
postprocess();
} catch (Exception ex) {
logger.log(Level.SEVERE, toolName + ": " + ex.getMessage(), ex);
}
}
}
/**
* This method is responsible for stopping the tool utility.
* @throws Exception Any exception thrown by the wrapped method
*/
public void stop() throws Exception {
stop(true);
}
/**
* This method is responsible for stopping the tool utility.
* @param warn Whether to warn if the tool already ended.
* @throws Exception Any exception thrown by the wrapped method
*/
protected void stop(boolean warn) throws Exception{
if (toolStatus == STARTED){
Invoker.setContextLocation(tc.toolPath);
try {
stopMethod.invoke(this.tool);
} finally {
Invoker.setContextLocation(null);
}
// saveToolLogs(tool.getInputStream(), tool.getErrorStream());
toolStatus = STOPPED;
logger.info("Stopped tool " + toolName);
} else if (warn && toolStatus == NOT_STARTED)
logger.warning("Tool not started but stop called for " + toolName);
}
/**
* Transfers the tool output file back to the master.
*/
protected void xferLog() {
String logfile = null;
try {
FileTransfer[] transfer;
if (outputHandle != null) {
logger.finer("outputHandle is not null. outfile is " + outfile);
transfer = new FileTransfer[1];
transfer[0] = outputHandle.fetchOutput(outputStream, outfile);
} else if (tc.localOutputFiles != null) {
transfer = new FileTransfer[tc.localOutputFiles.size()];
logger.finer("tc.localOutputFiles is not null. Size is " + tc.localOutputFiles.size());
int idx = 0;
for (Map.Entry<String, String> entry :
tc.localOutputFiles.entrySet()) {
String key = entry.getKey();
String path = entry.getValue();
String ext;
logger.finer("localOutputFiles path = " + path);
if (path.endsWith(".xan") || path.contains(".xan."))
ext = ".xan.";
else
ext = ".log.";
if (! new File(path).exists()) {
logger.warning(toolName + ": Transfer file " + path +
" not found.");
continue;
}
// The multiple files may simply be raw and xan. If so, treat as one
String outFile;
if ("raw".equals(key) || "xan".equals(key))
outFile = outDir + toolName + ext + host;
else
outFile = outDir + toolName + '-' + key + ext + host;
transfer[idx++] = new FileTransfer(path, outFile);
}
} else {
transfer = new FileTransfer[1];
logfile = tc.getOutputFile();
logger.finer("single file transfer. logfile is " + logfile);
if (!new File(logfile).exists()) {
logger.warning(toolName + ": Transfer file " + logfile +
" not found.");
return;
}
transfer[0] = new FileTransfer(logfile, outfile);
}
String s = Config.FILE_AGENT;
FileAgent fa = (FileAgent) CmdAgentImpl.getRegistry().getService(s);
for (FileTransfer t : transfer) {
logger.fine(toolName + ": Transferring log from " +
t.getSource() + " to " + t.getDest());
// Use FileAgent on master machine to copy log
if (t != null) {
if (fa.push(t) != t.getSize())
logger.info(toolName + ": Invalid transfer size");
}
}
} catch (IOException e) {
if (logfile == null)
logger.log(Level.INFO, toolName + ": Error transferring " +
logfile, e);
else
logger.log(Level.INFO, toolName + ": Error transferring " +
toolName + " output.", e);
}
}
/**
* Finishes up the tool and notifies the infrastructure of the tool
* finishing up.
*/
protected void finish() {
if (!countedDown)
latch.countDown();
countedDown = true;
}
/**
* This method is responsible for killing the tool utility.
* @throws Exception Any exception thrown by the wrapped method
*/
public void kill() throws Exception {
// For most tools, we try to collect the output no matter what.
stop(false);
finish();
}
}