// ============================================================================
//
// Copyright (C) 2006-2012 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.designer.core.runprocess;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.talend.commons.exception.SystemException;
import org.talend.commons.ui.runtime.exception.ExceptionHandler;
import org.talend.commons.ui.runtime.exception.MessageBoxExceptionHandler;
import org.talend.core.CorePlugin;
import org.talend.core.language.ECodeLanguage;
import org.talend.core.language.LanguageManager;
import org.talend.core.model.process.IContext;
import org.talend.core.model.process.IProcess;
import org.talend.core.model.process.ITargetExecutionConfig;
import org.talend.designer.codegen.ICodeGenerator;
import org.talend.designer.core.ISyntaxCheckableEditor;
import org.talend.designer.core.i18n.Messages;
import org.talend.designer.runprocess.IEclipseProcessor;
import org.talend.designer.runprocess.IProcessMessageManager;
import org.talend.designer.runprocess.IProcessor;
import org.talend.designer.runprocess.ProcessorException;
import org.talend.designer.runprocess.ProcessorUtilities;
import org.talend.repository.ui.wizards.exportjob.scriptsmanager.JobScriptsManager;
/**
* DOC nrousseau class global comment. Detailled comment <br/>
*
* $Id: Processor.java 52559 2010-12-13 04:14:06Z nrousseau $
*
*
*/
public abstract class Processor implements IProcessor, IEclipseProcessor {
private static Logger log = Logger.getLogger(Processor.class);
public static final String CTX_ARG = "--context="; //$NON-NLS-1$
private static final String STAT_PORT_ARG = "--stat_port="; //$NON-NLS-1$
private static final String TRACE_PORT_ARG = "--trace_port="; //$NON-NLS-1$
private static boolean externalUse = false;
protected IContext context;
private ITargetExecutionConfig targetExecutionConfig;
private String libraryPath;
private String interpreter;
private String codeLocation;
protected IProcess process;
protected IProject project;
/** Path to generated context code. */
protected IPath contextPath;
/** Path to generated perl code. */
protected IPath codePath;
protected String targetPlatform;
private boolean codeGenerated; // will say if the code has been generated at
private String[] proxyParameters;
// least once
/**
* Construct a new Processor.
*
* @param process
*
* @param process Process to be run.
*/
public Processor(IProcess process) {
super();
if (ProcessorUtilities.isExportConfig()) {
setInterpreter(ProcessorUtilities.getInterpreter());
setLibraryPath(ProcessorUtilities.getLibraryPath());
setCodeLocation(ProcessorUtilities.getCodeLocation());
}
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#run(int, int, java.lang.String)
*/
public Process run(int statisticsPort, int tracePort, String watchParam) throws ProcessorException {
return run(statisticsPort, tracePort, watchParam, null, null);
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#run(int, int, java.lang.String,
* org.eclipse.core.runtime.IProgressMonitor, org.talend.designer.runprocess.IProcessMessageManager)
*/
public Process run(int statisticsPort, int tracePort, String watchParam, IProgressMonitor monitor,
IProcessMessageManager processMessageManager) throws ProcessorException {
if (context == null) {
throw new IllegalStateException("Context is empty, context must be set before call"); //$NON-NLS-1$
}
setProcessorStates(STATES_RUNTIME);
if (!codeGenerated) {
codeGenerated = ProcessorUtilities.generateCode(process, context, statisticsPort != NO_STATISTICS,
tracePort != NO_TRACES, true) != null;
// if the code can't be generated by the ProcessorUtilities, then it
// will be generated by this way
// this will be used for example for the shadow process.
if (!codeGenerated) {
generateCode(statisticsPort != NO_STATISTICS, tracePort != NO_TRACES, true);
if (LanguageManager.getCurrentLanguage() == ECodeLanguage.JAVA) {
try {
CorePlugin.getDefault().getRunProcessService().getJavaProject().getProject()
.build(IncrementalProjectBuilder.AUTO_BUILD, null);
} catch (CoreException e) {
ExceptionHandler.process(e);
}
}
}
}
if (watchParam == null) {
// only works with context name and remove context interpereter
// option
return exec(Level.INFO, statisticsPort, tracePort);
}
return exec(Level.INFO, statisticsPort, tracePort, watchParam);
}
/**
* Debug the process using a given context.
*
* @param context Context to be used.
* @return The configuration to be launched in debug mode.
* @throws ProcessorException Process failed.
* @throws CoreException
* @throws ProcessorException
*/
public ILaunchConfiguration debug() throws ProcessorException {
if (context == null) {
throw new IllegalArgumentException("Context is empty, context must be set before call"); //$NON-NLS-1$
}
ILaunchConfiguration config = null;
try {
setProcessorStates(STATES_EDIT);
config = (ILaunchConfiguration) saveLaunchConfiguration();
} catch (CoreException ce) {
throw new ProcessorException(ce);
}
return config;
}
/**
*
* DOC xzhang Comment method "getDebugConfiguration". For the bug 5430
*
* @param statOption
* @param traceOption
* @param codeOptions
* @return
* @throws ProcessorException
*/
public ILaunchConfiguration getDebugConfiguration(int statOption, int traceOption, String... codeOptions)
throws ProcessorException {
if (context == null) {
throw new IllegalArgumentException("Context is empty, context must be set before call"); //$NON-NLS-1$
}
StringBuilder parameterStr = new StringBuilder(" "); //$NON-NLS-1$
if (codeOptions != null) {
for (int i = 0; i < codeOptions.length; i++) {
String string = codeOptions[i];
if (string != null) {
parameterStr.append(string).append(" "); //$NON-NLS-1$
}
}
}
if (statOption != -1) {
parameterStr = parameterStr.append(STAT_PORT_ARG + statOption).append(" "); //$NON-NLS-1$
}
if (traceOption != -1) {
parameterStr = parameterStr.append(TRACE_PORT_ARG + traceOption).append(" "); //$NON-NLS-1$
}
ILaunchConfiguration config = null;
try {
setProcessorStates(STATES_EDIT);
config = (ILaunchConfiguration) saveLaunchConfigurationWithParam(parameterStr.toString());
} catch (CoreException ce) {
throw new ProcessorException(ce);
}
return config;
}
/**
* Get the executable commandLine.
*
* @param contextName
* @param statOption
* @param traceOption
* @param codeOptions
* @return
*/
public String[] getCommandLine(boolean needContext, boolean externalUse, int statOption, int traceOption,
String... codeOptions) {
setExternalUse(externalUse);
String[] cmd = null;
try {
cmd = getCommandLine();
} catch (ProcessorException e) {
ExceptionHandler.process(e);
}
cmd = addCommmandLineAttch(needContext, cmd, context.getName(), statOption, traceOption, codeOptions);
// (feature 4258)
if (Platform.OS_LINUX.equals(getTargetPlatform())) {
// original is $*
cmd = (String[]) ArrayUtils.add(cmd, JobScriptsManager.CMDFORUNIX); //$NON-NLS-1$
} else if (Platform.OS_WIN32.equals(getTargetPlatform())) {
cmd = (String[]) ArrayUtils.add(cmd, JobScriptsManager.CMDFORWIN); //$NON-NLS-1$
}
return cmd;
}
/**
* Add the attchment condition to commmandline .
*
* @param commandLine
* @param contextName
* @param statOption
* @param traceOption
* @param codeOptions
* @return
*/
protected static String[] addCommmandLineAttch(boolean needContext, String[] commandLine, String contextName, int statOption,
int traceOption, String... codeOptions) {
String[] cmd = commandLine;
if (codeOptions != null) {
for (int i = 0; i < codeOptions.length; i++) {
String string = codeOptions[i];
if (string != null) {
cmd = (String[]) ArrayUtils.add(cmd, string);
}
}
}
if (needContext && contextName != null) {
cmd = (String[]) ArrayUtils.add(cmd, CTX_ARG + contextName);
}
if (statOption != -1) {
cmd = (String[]) ArrayUtils.add(cmd, STAT_PORT_ARG + statOption);
}
if (traceOption != -1) {
cmd = (String[]) ArrayUtils.add(cmd, TRACE_PORT_ARG + traceOption);
}
return cmd;
}
/**
* Code Execution, used, when you know where the code stands.
*
* @param Perl Absolute Code Path
* @param Context Name
* @param Port Statistics
* @param Port Trace
* @return Command Process Launched
* @throws ProcessorException
*/
private Process exec(Level level, int statOption, int traceOption, String... codeOptions) throws ProcessorException {
String[] cmd = getCommandLine(true, false, statOption, traceOption, codeOptions);
logCommandLine(cmd, level);
try {
return Runtime.getRuntime().exec(cmd);
} catch (IOException ioe) {
throw new ProcessorException(Messages.getString("Processor.execFailed"), ioe); //$NON-NLS-1$
}
}
public static Thread createProdConsThread(final InputStream input, final boolean isError, final int bufferSize,
final StringBuffer out, final StringBuffer err) {
Thread thread = new Thread() {
public void run() {
try {
BufferedInputStream outStreamProcess = new BufferedInputStream(input);
byte[] buffer = new byte[bufferSize];
int count = 0;
while ((count = outStreamProcess.read(buffer, 0, buffer.length)) != -1) {
if (isError) {
err.append(new String(buffer, 0, count));
} else {
out.append(new String(buffer, 0, count));
}
}
outStreamProcess.close();
} catch (IOException ioe) {
ExceptionHandler.process(ioe);
} finally {
try {
input.close();
} catch (IOException e) {
ExceptionHandler.process(e);
}
}
}
};
return thread;
}
public static void logCommandLine(String[] cmd, Level level) {
StringBuffer sb = new StringBuffer();
sb.append(Messages.getString("Processor.commandLineLog")); //$NON-NLS-1$
for (String s : cmd) {
sb.append(' ').append(s);
}
log.log(level, sb.toString());
}
/**
* Sets the externalUse.
*
* @param externalUse the externalUse to set
*/
public static void setExternalUse(boolean externalUse) {
Processor.externalUse = externalUse;
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getInterpreter()
*/
public String getInterpreter() throws ProcessorException {
return interpreter;
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#setInterpreter(java.lang.String )
*/
public void setInterpreter(String interpreter) {
this.interpreter = interpreter;
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#setLibraryPath(java.lang.String )
*/
public void setLibraryPath(String libraryPath) {
this.libraryPath = libraryPath;
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getCodeLocation()
*/
public String getCodeLocation() throws ProcessorException {
return codeLocation;
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#setCodeLocation(java.lang.String )
*/
public void setCodeLocation(String codeLocation) {
this.codeLocation = codeLocation;
}
public abstract void setSyntaxCheckableEditor(ISyntaxCheckableEditor editor);
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#generateCode(org.talend.core .model.process.IContext, boolean,
* boolean, boolean)
*/
public void generateCode(boolean statistics, boolean trace, boolean properties) throws ProcessorException {
if (context == null) {
throw new IllegalStateException("Context is empty, context must be set before call"); //$NON-NLS-1$
}
// Remove the synchronization of the routines when generate the code.
// This shouldn't be needed anymore.
// try {
// DesignerPlugin.getDefault().getCodeGeneratorService().
// createRoutineSynchronizer().syncAllRoutines();
// } catch (SystemException e) {
// throw new ProcessorException(e);
// }
codeGenerated = true; // set the flag to true to tell the code has been
// generated at least once.
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#generateCode(org.talend.core .model.process.IContext, boolean,
* boolean, boolean)
*/
public void generateCode(boolean statistics, boolean trace, boolean javaProperties, boolean exportAsOSGI)
throws ProcessorException {
if (context == null) {
throw new IllegalStateException("Context is empty, context must be set before call"); //$NON-NLS-1$
}
// Remove the synchronization of the routines when generate the code.
// This shouldn't be needed anymore.
// try {
// DesignerPlugin.getDefault().getCodeGeneratorService().
// createRoutineSynchronizer().syncAllRoutines();
// } catch (SystemException e) {
// throw new ProcessorException(e);
// }
codeGenerated = true; // set the flag to true to tell the code has been
// generated at least once.
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getCodeContext()
*/
public abstract String getCodeContext();
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getCodePath()
*/
public abstract IPath getCodePath();
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getCodeProject()
*/
public abstract IProject getCodeProject();
public abstract String[] getCommandLine() throws ProcessorException;
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getContextPath()
*/
public abstract IPath getContextPath();
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getLineNumber(java.lang.String)
*/
public abstract int getLineNumber(String nodeName);
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getProcessorType()
*/
public abstract String getProcessorType();
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#getTypeName()
*/
public abstract String getTypeName();
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#initPaths(org.talend.core.model .process.IContext)
*/
public abstract void initPaths(IContext context) throws ProcessorException;
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#saveLaunchConfiguration()
*/
public abstract Object saveLaunchConfiguration() throws CoreException;
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.Processor#saveLaunchConfigurationWithParam ()
*/
public abstract Object saveLaunchConfigurationWithParam(String parameterStr) throws CoreException;
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#setProcessorStates(java.lang .String)
*/
public abstract void setProcessorStates(int states);
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#setContext(org.talend.core. model.process.IContext)
*/
public void setContext(IContext context) {
if (context == null) {
// usefull to generate a commandline only (from ProcessorUtilities)
return;
}
try {
initPaths(context);
} catch (ProcessorException pe) {
MessageBoxExceptionHandler.process(pe);
}
this.context = context;
}
protected void updateContextCode(ICodeGenerator codeGen) throws ProcessorException {
if (codeGen == null) {
return;
}
try {
String processContext = "false"; //$NON-NLS-1$
try {
processContext = codeGen.generateContextCode(context);
} catch (SystemException e) {
throw new ProcessorException(Messages.getString("Processor.generationFailed"), e); //$NON-NLS-1$
}
// IFile contextFile = javaProject.getFile(contextPath);
IFile contextFile = this.project.getProject().getFile(contextPath);
InputStream contextStream = new ByteArrayInputStream(processContext.getBytes());
if (!contextFile.exists()) {
// see bug 0003592, detele file with different case in windows
deleteFileIfExisted(contextFile);
contextFile.create(contextStream, true, null);
} else {
contextFile.setContents(contextStream, true, false, null);
}
} catch (CoreException e1) {
throw new ProcessorException(Messages.getString("Processor.tempFailed"), e1); //$NON-NLS-1$
}
}
public ITargetExecutionConfig getTargetExecutionConfig() {
return this.targetExecutionConfig;
}
public void setTargetExecutionConfig(ITargetExecutionConfig serverConfiguration) {
this.targetExecutionConfig = serverConfiguration;
}
public IProcess getProcess() {
return this.process;
}
public IContext getContext() {
return this.context;
}
public String getTargetPlatform() {
return targetPlatform;
}
public void setTargetPlatform(String targetPlatform) {
this.targetPlatform = targetPlatform;
}
/**
* DOC bqian Comment method "replaceSnippet".
*
* @param processCode
*/
public String replaceSnippet(String processCode) {
SnippetParser sp = new SnippetParser();
return sp.convertSnippet(processCode);
}
public boolean checkKillAllowed() {
return true;
}
/**
* Delete file from the file system if there is another file with different case (lowercase or uppercase) which may
* cause problem in windows system. See bug 0003592 for more detail.
*
* @param codeFile The file that contains source codes that are generated by tos.
* @throws CoreException
*/
protected void deleteFileIfExisted(IFile codeFile) throws CoreException {
File systemFile = codeFile.getLocation().toFile();
if (systemFile.exists()) {
systemFile.delete();
codeFile.getParent().refreshLocal(IResource.DEPTH_INFINITE, null);
}
}
/**
* Check if the code has been generated at least once. Will be false if the code has never been generated.
*
* @return boolean to tell if any code has been generated already or not for this job.
*/
public boolean isCodeGenerated() {
return this.codeGenerated;
}
/**
* Add the possibility to force the flag for the code generated. <br>
* This can be usefull to force to generate the code.
*
* @param codeGenerated boolean to tell if any code has been generated already or not for this job.
*/
public void setCodeGenerated(boolean codeGenerated) {
this.codeGenerated = codeGenerated;
}
/*
* (non-Javadoc)
*
* @see org.talend.designer.runprocess.IProcessor#generateContextCode()
*/
abstract public void generateContextCode() throws ProcessorException;
public String[] getProxyParameters() {
return this.proxyParameters;
}
public void setProxyParameters(String[] proxyParameters) {
if (proxyParameters != null) {
this.proxyParameters = proxyParameters;
}
}
}