package tool.builder;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import tool.ToolBuilderActivator;
import tool.builder.resource.RepositoryComponent;
import tool.model.SupplierPlan;
import tool.model.ToolPlan;
public class FScript {
public static final String DEFAULT_LOG_FLAGS = "%stdout(err:sh:* cfg:c4:23:* cfg:os:21 trc:ui:10:1)";
@SuppressWarnings("serial")
public static class FScriptException extends RuntimeException {
public FScriptException(String message) {
super(message);
}
}
private String repository;
private String workspace;
private String workspacepassword;
private String logFlags;
private boolean workspaceOpen;
private BufferedWriter toFscript;
private BufferedReader fromFscript;
private BufferedReader errorFscript;
private boolean connectedToFscript = false;
private String forteRoot;
protected Process fscriptProcess;
private boolean justRefreshed = false;
// protected RepositorySession session;
protected boolean useSession = false;
private static final String USE_SESSION =
"Tool/debug/useSession";
private static final String CONSOLE_NAME = "Repository Interface";
protected MessageConsole msgConsole;
protected MessageConsoleStream msgStream;
private FScriptSchedulingRule rule = new FScriptSchedulingRule();
/**
* This class is designed to redirect the contents of a file to a new processes standard input. Note
* that the new process accepts the input, and hence gives us an output stream we write to in this
* process, which will become an input stream (or similar) in the new child process.
* @author Tim
*/
public static class OutputStreamHandler implements Runnable {
private OutputStream stream;
private String command;
public OutputStreamHandler(OutputStream stream, String command) {
this.stream = stream;
this.command = command;
}
public void run() {
try {
// Wait for there to be input in the container
BufferedOutputStream bos = new BufferedOutputStream(stream);
bos.write(command.getBytes());
bos.close();
}
catch (IOException e) {
ToolBuilderActivator.log(IStatus.ERROR,"Error redirecting input to new process.", e);
}
}
}
public FScript(String forteRoot){
super();
this.forteRoot = forteRoot;
this.msgConsole = ToolBuilderActivator.findConsole(CONSOLE_NAME);
if (this.msgConsole != null)
clearConsole();
msgStream = msgConsole.newMessageStream();
}
public FScript(String forteRoot, String repository, String workspace, String workspacePassword, String logFlags, boolean useSession){
this(forteRoot);
this.repository = repository;
this.workspace = workspace;
this.workspacepassword = workspacePassword;
this.logFlags = logFlags;
this.useSession = useSession;
}
protected void writeToConsole(String message){
if (this.msgStream != null)
this.msgStream.println(message);
}
protected void clearConsole(){
if (this.msgConsole != null)
msgConsole.clearConsole();
}
public synchronized String processFscriptCommand(String fscriptCMD) throws ToolSystemException{
String result = "";
clearConsole();
try {
String cmd = "";
String osName = System.getProperty("os.name");
if (osName.startsWith("Windows")){
cmd = "cmd.exe /C ";
} else {
writeToConsole("Connection to a repository is only possible on a windows platform");
return "";
}
IPath ftExecPath = new Path(this.forteRoot);
ftExecPath = ftExecPath.append("install");
ftExecPath = ftExecPath.append("bin");
ftExecPath = ftExecPath.append("ftexec");
IPath fscriptPath = new Path(this.forteRoot);
fscriptPath = fscriptPath.append("userapp");
fscriptPath = fscriptPath.append("fscript");
fscriptPath = fscriptPath.append("cl12");
fscriptPath = fscriptPath.append("fscript");
cmd = cmd + ftExecPath.toPortableString() + " -fcons -fnict -fi bt:"+ fscriptPath.toPortableString() + " -fs -fm(n:10000,x:50000) -fr " + repository + " -fw " + workspace + " -fl " + this.logFlags;
fscriptProcess = Runtime.getRuntime().exec(cmd);
// Start readers to consume output. It looks very innocuous with the output from the process
// being mapped to p.inputStream, but this is correct.
OutputContainer container = new OutputContainer(this.msgStream);
new Thread(new InputStreamHandler(fscriptProcess.getInputStream(), container, false), "OutputFileHandler").start();
new Thread(new InputStreamHandler(fscriptProcess.getErrorStream(), container, true), "ErrorFileHandler").start();
new Thread(new OutputStreamHandler(fscriptProcess.getOutputStream(), fscriptCMD), "InputFileHandler").start();
fscriptProcess.waitFor();
this.connectedToFscript = true;
if (container.stderr.length() > 0) {
throw new FScriptException(container.stderr.toString());
}
else {
result = container.stdout.toString();
if (result.contains("SYSTEM ERROR")){
String errorText = result.substring(result.indexOf("SYSTEM ERROR"));
throw new ToolSystemException(getRepository(), errorText);
}
}
} catch (IOException e) {
ToolBuilderActivator.showError("Error connecting to fscript.", e);
} catch (InterruptedException e) {
ToolBuilderActivator.showError("Error connecting to fscript.", e);
}
return result;
}
public void disconnect(){
try {
if (fromFscript != null){
fromFscript.close();
fromFscript = null;
}
if (fromFscript != null){
fromFscript.close();
}
if (errorFscript != null){
errorFscript.close();
}
if (fscriptProcess != null){
fscriptProcess.waitFor();
fscriptProcess = null;
}
} catch (IOException e) {
ToolBuilderActivator.log(IStatus.ERROR,"Error disconnecting from fscript.", e);
} catch (InterruptedException e) {
ToolBuilderActivator.log(IStatus.ERROR,"Error disconnecting from fscript.", e);
} finally {
this.connectedToFscript = false;
}
}
public String SCMExportWorkspace(String directory) throws InvalidToolRepositoryException, ToolSystemException{
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("SCMExportWorkspace " + directory );
String command = "open\r\n" +
"SCMExportWork " + directory + "\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
disconnect();
return result;
}
private static final Pattern syntaxErrorPattern = Pattern.compile("^(.+?):(.+?):error:Line\\s+(\\d+):(.+?)$",Pattern.MULTILINE);
public String compilePlanComponent(String plan, IFile file, FscriptErrorHandler reporter) throws InvalidToolRepositoryException, ToolSystemException{
if (!isValid()){
return "Can only run on Windows"; //throw new InvalidToolRepositoryException(getRepository());
}
IPath compPath = file.getRawLocation().makeAbsolute();
writeToConsole("CompilePlanComponent " + plan + "." + file.getName());
String command = "open\r\n" +
"findPlan " + plan + "\r\n" +
"ImportClass " + compPath.toOSString() + "\r\n" +
"commit\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
if (result.contains("ERROR: Errors in compile")){
/*
* syntax errors
*/
Matcher matcher = syntaxErrorPattern.matcher(result);
while (matcher.find()){
// String wholeMatch = matcher.group(0);
int line = Integer.parseInt(matcher.group(3));
String message = matcher.group(4);
reporter.addError(message, line);
}
} else if (result.contains("USER ERROR:")){
ToolBuilderActivator.showError(file.getName() + " must be checked out or branched before it can be changed", null);
}
disconnect();
return result;
}
public String checkoutPlanComponent(String plan, String component) throws InvalidToolRepositoryException, ToolSystemException{
if (!isValid()){
return "Can only run on Windows"; //throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("CheckoutPlanComponent " + plan + "." + component);
String command = "open\r\n" +
"findPlan " + plan + "\r\n" +
"CheckoutComp " + component + "\r\n" +
"commit\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
if (result.contains("USER ERROR:")){
ToolBuilderActivator.showError(component + " cannot be checked out", null);
}
disconnect();
return result;
}
public String[] ListPlans(boolean pIncludeSystem,
boolean pIncludeLibraries,
boolean Topo,
boolean pIncludeInternal) throws InvalidToolRepositoryException, ToolSystemException{
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("ListPlans");
String[] names = null;
String command = "open\r\n" +
"ListProj\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
disconnect();
result = result.substring(result.indexOf("Projects:"), result.lastIndexOf("fscript"));
String[] lines = result.split("\r\n");
names = new String[lines.length-1];
for (int i = 1; i < lines.length; i++){
names[i-1] = lines[i].trim();
}
return names;
}
public String[] ListWorkspaces() throws ToolSystemException, InvalidToolRepositoryException {
writeToConsole("ListWorkspaces");
String command = "Listwork\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
disconnect();
result = result.substring(result.indexOf("Available workspaces:"), result.lastIndexOf("fscript"));
String[] lines = result.split("\r\n");
String[] names = new String[lines.length-1];
for (int i = 1; i < lines.length; i++){
names[i-1] = lines[i].trim();
}
return names;
}
private static Pattern changedPlans = Pattern.compile("Matching components for Project (.+?):");
private static Pattern changedComps = Pattern.compile("^\\s+(\\S+?)\\s\\-\\s(checked\\-out|new|branched|read\\-only)");
public Map<String, RepositoryComponent> ListWorkspaceChanges() throws ToolSystemException, InvalidToolRepositoryException {
writeToConsole("ListWorkspaceChanges");
String command = "open\r\n" +
"ListChangesInWorkspace bcnr\r\n" +
"close\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
disconnect();
// result = result.substring(result.indexOf("Available workspaces:"), result.lastIndexOf("fscript"));
String[] lines = result.split("\r\n");
Map<String, RepositoryComponent> comps = new HashMap<String, RepositoryComponent>();
String currentPlan = null;
for (String line : lines){
Matcher planMatcher = changedPlans.matcher(line);
if (planMatcher.find()){
currentPlan = planMatcher.group(1);
continue;
}
Matcher compMatcher = changedComps.matcher(line);
if (compMatcher.find()){
String name = compMatcher.group(1);
String statusString = compMatcher.group(2);
comps.put(currentPlan + "." + name, new RepositoryComponent(currentPlan, name, statusString));
}
}
return comps;
}
public String executeCMD(String command) throws ToolSystemException, InvalidToolRepositoryException {
writeToConsole("executeCMD");
String result = processFscriptCommand(command);
disconnect();
return result;
}
public void close() {
// if (this.session != null){
// try {
// this.session.closeWorkspace();
// this.session.closeRepository();
// } catch (NotConnectedException e) {
// ToolBuilderActivator.showError("Error closing Tool Respository", e);
// } catch (CommandException e) {
// ToolBuilderActivator.showError("Error closing Tool Respository", e);
// }
// }
}
public String ShowPlan(String planName) throws InvalidToolRepositoryException, ToolSystemException{
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("ShowPlan " + planName);
String command = "open\r\n" +
"FindProj " + planName + "\r\n" +
"ShowPlan\r\n" +
"close\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
writeToConsole("ShowPlan output:\n" + result);
int start = result.indexOf("Project:");
int end = result.lastIndexOf("fscript");
writeToConsole("Start: " + start + " End: " + end);
result = result.substring(start, end);
disconnect();
return result;
}
private static Pattern isProjectpattern = Pattern.compile("(?s)Application Type: USER.+Portable 4GL scope(?-s)", Pattern.MULTILINE);
public boolean isProject(String planName) throws InvalidToolRepositoryException, ToolSystemException{
if (isForteLibrary(planName))
return false;
String planText = ShowPlan(planName);
Matcher matcher = isProjectpattern.matcher(planText);
boolean isProject = matcher.find();
return isProject;
}
public boolean isForteLibrary(String name){
boolean isForte = false;
for (String libName : ToolPlan.forteLibrariesList){
if (libName.equalsIgnoreCase(name)){
isForte = true;
break;
}
}
return isForte;
}
public String exportForteLibraries(String directory) throws InvalidToolRepositoryException, ToolSystemException{
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("exportForteLibraries to " + directory);
StringBuilder sb = new StringBuilder("open\r\n");
for (String libName : ToolPlan.forteLibrariesList){
sb.append("FindPlan ");
sb.append(libName);
sb.append("\r\n");
sb.append("ExportPlan ");
sb.append(directory);
sb.append("\\");
sb.append(libName);
sb.append(".pex\r\n");
}
sb.append("exit\r\n");
String command = sb.toString();
String result = processFscriptCommand(command);
disconnect();
return result;
}
public String exportAsPEX(String planName, String file) throws ToolRepositoryException {
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("exportAsPEX " + planName + " to " + file);
String command = "open\r\n" +
"FindPlan " + planName + "\r\n" +
"ExportPlan " + file + ".pex\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
disconnect();
return result;
}
// public String exportProject(String planName, String directory) throws InvalidToolRepositoryException, NotConnectedException, CommandException{
// if (!isValid()){
// throw new InvalidToolRepositoryException(getRepository());
// }
// writeToConsole("ExportProject " + planName + " to " + directory);
// RepositorySession session = getSession();
// String result = session.exportPlanAndComponents(planName, directory, true);
// return result;
// }
public String SCMExportProject(String planName, String directory) throws InvalidToolRepositoryException, ToolSystemException {
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("SCMExportProject " + planName + " to " + directory);
String command = "open\r\n" +
"findplan " + planName + "\r\n" +
"SCMExportProject " + directory + " all\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
disconnect();
return result;
}
/**
* runs a specified plan in the repository.
* It is run through fscript in stand-alone mode and as a separate process
* @param planName
* @return
* @throws ToolRepositoryException
*/
public String runPlan(String planName) throws ToolRepositoryException {
if (!isValid()){
throw new InvalidToolRepositoryException(getRepository());
}
writeToConsole("runPlan " + planName + " in Working Directory " + System.getProperty("user.dir", ""));
String command = "open\r\n" +
"FindPlan " + planName + "\r\n" +
"run\r\n" +
"exit\r\n";
String result = processFscriptCommand(command);
if (result.contains("USER ERROR:")){
ToolBuilderActivator.showError(planName + " must have a StartClass and StartMethod to be run", null);
}
disconnect();
return result;
}
public String getRepository() {
return repository;
}
public String getWorkspace() {
return workspace;
}
public boolean isWorkspaceOpen() {
return workspaceOpen;
}
public boolean isValid(){
String osName = System.getProperty("os.name");
return (osName.startsWith("Windows"));
}
public boolean isJustRefreshed() {
return justRefreshed;
}
public void setJustRefreshed(boolean justRefreshed) {
this.justRefreshed = justRefreshed;
}
protected void finalize() throws Throwable {
// if (this.session != null){
// this.session.terminate();
// }
};
/**
* Terminates the operating system process running fscript
*/
public void kill(){
if (this.fscriptProcess != null)
this.fscriptProcess.destroy();
}
public static void main(String args[]){
FScript r = new FScript("c:\\APPS\\forte_peter");
// r.repository = "bt:C:\\APPS\\Forte_peter\\repos\\pmilne_star_dev_6";
// r.workspace = "pmilne_star_dev_6";
r.repository = "bt:C:\\APPS\\Forte_peter\\repos\\Tests";
r.workspace = "empty";
r.logFlags = DEFAULT_LOG_FLAGS;
try {
// String[] ws = r.ListWorkspaces();
String[] plans = r.ListPlans(false, false, false, false);
// for (String planName : plans){
// if (r.isProject(planName)){
// String dir = "C:\\APPS\\DummyProject\\Projects\\" + planName;
// new File(dir).mkdirs();
// r.SCMExportProject(planName, dir);
// } else {
// String dir = "C:\\APPS\\DummyProject\\Libraries\\" + planName;
// r.exportAsPEX(planName, dir);
// }
// }
// r.exportForteLibraries("C:\\APPS\\DummyProject\\Libraries");
} catch (InvalidToolRepositoryException e) {
ToolBuilderActivator.showError("Error testing Tool Respository", e);
} catch (ToolSystemException e) {
ToolBuilderActivator.showError("Error testing Tool Respository", e);
}
}
// /**
// * create a command string using a string template
// * @param repos
// * @param workspace
// * @param wexPath
// * @return
// */
// public String createCommand(String templateFileName, String templateNam, TemplateAttribute ... attributes){
// FileReader reader;
// File templateFile;
// String output = "";
// try {
// URL url = Platform.getBundle(ToolBuilderActivator.PLUGIN_ID).getEntry("StringTemplates/Tool/" + templateFileName);
// templateFile = new File(FileLocator.toFileURL(url).toURI());
// reader = new FileReader(templateFile);
// StringTemplateGroup templates = new StringTemplateGroup(reader);
// StringTemplate wexTemplate = templates.lookupTemplate(templateNam);
// for (TemplateAttribute att : attributes){
// wexTemplate.setAttribute(att.name, att.value);
// }
// output = wexTemplate.toString();
// } catch (FileNotFoundException e) {
// reportError(e);
// } catch (URISyntaxException e) {
// reportError(e);
// } catch (IOException e) {
// reportError(e);
// }
// return output;
//
// }
protected void reportError(Throwable e){
if (ToolBuilderActivator.getDefault() != null)
ToolBuilderActivator.showError("Error running Fscript", e);
else
e.printStackTrace();
}
public static FScript getFScript(IResource resource) throws CoreException{
IProject project = resource.getProject();
FScript fscript = (FScript) project.getSessionProperty(ToolNature.fscriptInstanceQualifiedName);
if (fscript == null){
fscript = new FScript(project.getPersistentProperty(ToolNature.forteRootQualifiedName),
project.getPersistentProperty(ToolNature.reposQualifiedName),
project.getPersistentProperty(ToolNature.workspaceQualifiedName),
project.getPersistentProperty(ToolNature.worspacePasswordQualifiedName),
project.getPersistentProperty(ToolNature.loggerQualifiedName),
false);
project.setSessionProperty(ToolNature.fscriptInstanceQualifiedName, fscript);
}
return fscript;
}
public FScriptSchedulingRule getRule() {
return rule;
}
}