/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* TargetRunner.java
* Creation date: (5/8/01 5:40:36 PM)
* By: Bo Ilic
*/
package org.openquark.cal.valuenode;
import java.util.logging.Level;
import org.openquark.cal.compiler.AdjunctSource;
import org.openquark.cal.compiler.CompilerMessage;
import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.MessageKind;
import org.openquark.cal.compiler.MessageLogger;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.TypeChecker;
import org.openquark.cal.compiler.TypeException;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.compiler.io.EntryPoint;
import org.openquark.cal.compiler.io.EntryPointSpec;
import org.openquark.cal.compiler.io.InputPolicy;
import org.openquark.cal.compiler.io.OutputPolicy;
import org.openquark.cal.machine.StatsGenerator;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.services.WorkspaceManager;
/**
* A TargetRunner runs CAL code and returns a value node.
* Creation date: (5/8/01 5:40:36 PM)
* @author Bo Ilic
*/
public abstract class TargetRunner extends CALRunner {
/** The target to run. */
private Target target;
private EntryPoint entryPoint;
/** The module in which to run the target. */
private ModuleName targetModule;
/** The arguments to the target when running. */
private Object[] entryPointArguments;
// An object to register with the executor to generate
// running time statistics.
protected StatsGenerator execTimeGen = null;
private boolean showConsoleInfo = true;
private ValueNode outputValueNode;
protected CALExecutorException error;
protected Object executionResult;
/**
* A ProgramCompileException is an exception which signifies a failure to compile a program.
* Creation date: (Jun 17, 2002 4:38:12 PM)
* @author Edward Lam
*/
public static class ProgramCompileException extends Exception {
private static final long serialVersionUID = -3009322703048839008L;
/** The maximum error severity returned by the compile. */
private final CompilerMessage.Severity maxErrorSeverity;
/** The first compiler error returned by the compile. */
private final CompilerMessage firstError;
/**
* Constructor for a ProgramCompileException.
* @param errSev The error severity returned by the compile.
* @param firstError The first compiler error returned by the compile.
*/
public ProgramCompileException(CompilerMessage.Severity errSev, CompilerMessage firstError) {
if (firstError == null) {
throw new NullPointerException ("ProgramCompileException: firstError must not be null.");
}
this.maxErrorSeverity = errSev;
this.firstError = firstError;
}
/**
* Get the error severity code returned by the compile in question.
* @return ErrorMessage.Severity the error severity returned by the compile.
*/
public CompilerMessage.Severity getMaxErrorSeverity() {
return maxErrorSeverity;
}
/**
* @return CompilerMessage The first compiler error returned by the compile.
*/
public CompilerMessage getFirstError() {
return firstError;
}
}
/**
* Constructor for a TargetRunner
* @param workspaceManager the workspaceManager for this runner.
*/
public TargetRunner(WorkspaceManager workspaceManager) {
super(workspaceManager);
}
/**
* Set the target being run by this target runner.
* @param target the target to run.
* @param targetModule the module in which the target exists.
*/
protected void setTarget(Target target, ModuleName targetModule) {
this.target = target;
this.targetModule = targetModule;
}
/**
* @return the target used by this runner
*/
protected final Target getTarget() {
return target;
}
/**
* @return the output value node.
*/
protected final ValueNode getOutputValueNode() {
return outputValueNode;
}
/**
* Get the type checker associated with this target runner.
* @return TypeChecker the type checker for this target runner.
*/
final protected TypeChecker getTypeChecker() {
return getWorkspaceManager().getTypeChecker();
}
/**
* Get the name of the current target module.
* @return the name of the current target module.
*/
final protected ModuleName getTargetModule() {
return targetModule;
}
/**
* Returns a new TypeExpr for the current target, module, and typeChecker.
* @return TypeExpr a new TypeExpr for the target sc.
*/
protected final TypeExpr getNewTargetTypeExpr() {
return getNewTargetTypeExpr(target, targetModule, getTypeChecker());
}
/**
* Returns a new TypeExpr for the target.
* @param target Target the target for which to get a new type expression
* @param moduleName the name of the module in which the graph exists
* @param typeChecker TypeChecker the type checker to use to generate the type
* @return TypeExpr a new TypeExpr for the target sc.
*/
protected static final TypeExpr getNewTargetTypeExpr(Target target, ModuleName moduleName, TypeChecker typeChecker) {
AdjunctSource scDefinition = target.getTargetDef(null, typeChecker.getTypeCheckInfo(moduleName).getModuleTypeInfo());
CompilerMessageLogger logger = new MessageLogger ();
return typeChecker.checkFunction(scDefinition, moduleName, logger);
}
/**
* Prepare to perform a test run by compiling the Gem we're to test.
*/
protected void buildTestProgram(InputPolicy[] inputPolicies, TypeExpr[] argTypes) throws ProgramCompileException {
entryPoint = null;
if (inputPolicies == null) {
inputPolicies = new InputPolicy[0];
}
// Get supercombinator defined at the current target and its name
ModuleTypeInfo currentModuleTypeInfo = getWorkspaceManager().getWorkspace().getMetaModule(getTargetModule()).getTypeInfo();
AdjunctSource scDef = target.getTargetDef(null, currentModuleTypeInfo);
CompilerMessageLogger logger = new MessageLogger ();
TypeExpr targetTypeExpr = getTypeChecker().checkFunction(scDef, targetModule, logger);
if (targetTypeExpr == null) {
VALUENODE_LOGGER.log(Level.SEVERE, "Error determining gem execution type. Text: \n" + scDef);
throw new ProgramCompileException(CompilerMessage.Severity.ERROR, logger.getFirstError());
}
// Determine the overall output TypeExpr of the target.
int numArgs = inputPolicies.length;
TypeExpr[] targetTypePieces = targetTypeExpr.getTypePieces(numArgs);
TypeExpr outputTypeExpr;
try {
TypeExpr[] specializedTargetTypePieces =
TypeExpr.patternMatchPieces(argTypes, targetTypePieces, currentModuleTypeInfo);
outputTypeExpr = specializedTargetTypePieces[numArgs];
} catch (TypeException e) {
// What to do? You really don't want to be throwing an uncaught exception here.
VALUENODE_LOGGER.log(Level.WARNING, "Error determining gem execution output type.");
e.printStackTrace();
outputTypeExpr = targetTypePieces[numArgs];
}
TypeExpr declaredType = outputTypeExpr;
for (int i = numArgs - 1; i >= 0 ; i--) {
declaredType = TypeExpr.makeFunType(argTypes[i], declaredType);
}
// Update the scDef with the type declaration.
scDef = target.getTargetDef(declaredType, currentModuleTypeInfo);
outputValueNode = valueNodeBuilderHelper.getValueNodeForTypeExpr(outputTypeExpr);
if (outputValueNode == null) {
// Unable to create an output value node for type: {declaredType}
throw new ProgramCompileException(CompilerMessage.Severity.ERROR,
new CompilerMessage(new MessageKind.Error.UnableToCreateOutputValueNode(declaredType.toString())));
}
OutputPolicy outputPolicy = null;
outputPolicy = outputValueNode.getOutputPolicy();
if (outputPolicy == null) {
// Unable to retrieve an output policy for type: {declaredType}
throw new ProgramCompileException(CompilerMessage.Severity.ERROR,
new CompilerMessage(new MessageKind.Error.UnableToRetrieveAnOutputPolicy(declaredType.toString())));
}
if (getShowConsoleInfo()) {
System.out.println("Executing:\n" + scDef);
}
// Compile the definition, indicating that this is an adjunct
QualifiedName targetName = QualifiedName.make(getTargetModule(), getTarget().getTargetName(currentModuleTypeInfo));
entryPoint = getWorkspaceManager().getCompiler().getEntryPoint(scDef, EntryPointSpec.make(targetName, inputPolicies, outputPolicy), targetName.getModuleName(), logger);
if (entryPoint == null) {
throw new ProgramCompileException(CompilerMessage.Severity.ERROR, logger.getFirstError());
}
}
protected final void executeTestProgram(Object[] rpArguments, boolean threaded) {
this.entryPointArguments = new Object[rpArguments.length];
System.arraycopy(rpArguments, 0, this.entryPointArguments, 0, rpArguments.length);
startExecutingTestProgram(threaded);
}
private void startExecutingTestProgram(boolean threaded) {
// Instantiate a runtime and execute the program
runtime = createExecutor(getWorkspaceManager(), getWorkspaceManager().makeExecutionContextWithDefaultProperties());
execTimeGen = new StatsGenerator ();
runtime.addStatsGenerator(execTimeGen);
if (threaded) {
// Execute in a new thread
Thread execThread = new Thread(this, "CAL Execution Thread");
execThread.start();
} else {
// Execute in this thread
run();
}
}
/**
* Determine if this gem runner is in the midst of execution.
* @return boolean true if executing (eg. sleeping while waiting for VEPs to be filled in)
*/
boolean isExecuting() {
// executing if there's a runtime
return (runtime != null);
}
/**
* Simply call the executor to run the program
*/
@Override
public void run() {
// Call any setup methods before entering run state
enterRunningState();
error = null;
executionResult = null;
long start = System.currentTimeMillis();
try {
executionResult = runtime.exec(entryPoint, entryPointArguments);
} catch (CALExecutorException e) {
// Raise an INFO message
// DIAG
VALUENODE_LOGGER.log(Level.FINE, "Executor: " + e);
error = e;
//
// If some other error occurs, we could display this to the user in a dialog
//
} finally {
long end = System.currentTimeMillis();
getWorkspaceManager().resetCachedResults(runtime.getContext());
if (getShowConsoleInfo()) {
if (execTimeGen != null) {
for(final StatsGenerator.StatsObject stat : execTimeGen.getStatistics()) {
System.out.println (stat.generateShortMessage());
}
} else {
System.out.println ("time: " + (end - start));
}
}
// Call any cleanup methods to exit the run state
try {
exitRunningState();
} catch (Throwable t) {
t.printStackTrace(); // what else to do?
}
// Clear the runtime
runtime = null;
}
}
/**
* This method is called when the runtime is about to start evaluating.
*/
public void enterRunningState() {
}
/**
* This method is called when the runtime is done evaluation.
* When this method is called, we are still technically in the "running state" (the runtime is still available).
*/
public void exitRunningState() {
}
/**
* Terminate an execution. eg. User presses the Stop button.
*/
protected void stopExecution() {
// Notify the runtime that it should quit. (quits if the runtime is in the middle of execution)
if (runtime != null) {
runtime.requestQuit();
}
}
public final void setShowConsoleInfo (boolean b) {
showConsoleInfo = b;
}
public final boolean getShowConsoleInfo () {
return showConsoleInfo;
}
/**
* Returns true if an error has been encountered running the program.
*/
@Override
public boolean isErrorFlagged() {
return error != null;
}
}