/**
* $Id$
* $Date$
*
*/
package org.xmlsh.core;
import java.io.File;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xmlsh.sh.core.SourceLocation;
import org.xmlsh.sh.module.IModule;
import org.xmlsh.sh.shell.SerializeOpts;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.sh.shell.ShellConstants;
import org.xmlsh.util.PortCopier;
import org.xmlsh.util.StreamCopier;
import org.xmlsh.util.Util;
public class ExternalCommand implements ICommand {
private static Logger mLogger = LogManager.getLogger(ExternalCommand.class);
private File mCommandFile; // command path
private SourceLocation mLocation;
private IModule mModule;
@Override
protected void finalize() {
// Clear refs
mCommandFile = null;
mLocation = null;
mModule = null;
}
public ExternalCommand(File cmd, SourceLocation location, IModule module) {
mLogger.entry(cmd, location, module);
assert (module != null);
mCommandFile = cmd;
mLocation = location;
mModule = module;
}
@Override
public URL getURL() throws MalformedURLException {
return mCommandFile.toURI().toURL();
}
@Override
public int run(Shell shell, String cmd, List<XValue> args) throws Exception {
File curdir = Shell.getCurdir().getAbsoluteFile();
mLogger.debug("Run external command: " + mCommandFile.getPath()
+ " in directory: " + curdir.getPath());
ArrayList<XValue> cmdlist = new ArrayList<XValue>();
cmdlist.add(XValue.newXValue(mCommandFile.getPath()));
cmdlist.addAll(Util.expandSequences(args));
Process proc = null;
synchronized(this.getClass()) {
ProcessBuilder builder = new ProcessBuilder();
builder.command(Util.toStringList(cmdlist));
builder.directory(curdir);
setEnvironment(shell, builder);
proc = builder.start();
if(proc == null)
return -1;
shell.addChildProcess(proc);
}
// Start copiers for stdout, stderr
SerializeOpts serializeOpts = shell.getSerializeOpts();
StreamCopier outCopier = new StreamCopier(cmd + "-out",
proc.getInputStream(), shell.getEnv().getStdout()
.asOutputStream(serializeOpts));
StreamCopier errCopier = new StreamCopier(cmd + "-err",
proc.getErrorStream(), shell.getEnv().getStderr()
.asOutputStream(serializeOpts));
PortCopier inCopier = null;
if(!shell.getEnv().isStdinSystem())
inCopier = new PortCopier(cmd + "-in", shell.getEnv().getStdin(),
proc.getOutputStream(), serializeOpts);
else
proc.getOutputStream().close();
errCopier.start();
if(inCopier != null)
inCopier.start();
// outCopier.start();
outCopier.run(); // In place
// Close input just in case we have a broken pipe
outCopier.closeIn();
int ret;
try {
ret = proc.waitFor();
} catch (InterruptedException e) {
mLogger.warn("Interrupted waiting for process to complete: ", e);
ret = -1;
}
shell.removeChildProcess(proc);
// Kill off the input copier
// inCopier.interrupt();
// Process has exited, close the output
if(inCopier != null) {
// Input copier is pointless - try to close it but dont worry if you
// cant
// This may interrupt the copier
inCopier.close();
}
outCopier.join();
outCopier.close();
errCopier.join();
errCopier.close();
return ret;
}
/*
* Set the environment for a subprocess by the following
*
* 1) If a variable does not exist in the shell then delete it
* 2) For any "EXPORT" variables which are atomic Update any existing
* variables with new content from the shell
* 3) Add any unset "EXPORT" atomic variables
* 4) PATH and XPATH are re-set by re-serializing the sequence using the path
* seperator
* and the native directory seperator
*/
private void setEnvironment(Shell shell, ProcessBuilder builder) {
XEnvironment xenv = shell.getEnv();
Map<String, String> env = builder.environment();
if(env == null)
return;
// 1) delete any env vars not in the shell
// Use Iterator so we can call remove
Iterator<Entry<String, String>> iter = env.entrySet().iterator();
while(iter.hasNext()) {
Entry<String, String> e = iter.next();
String name = e.getKey();
// Remove PATH and XPATH as well as non-defined names
if(Util.isPath(name) || !xenv.isDefined(name))
iter.remove();
}
/*
* 2) For any "EXPORT" variables Update any existing variables with new
* content from the shell
* 3) Add any unset "EXPORT" variables of type string
*/
for(String name : xenv.getVarNames()) {
if(!Util.isPath(name)) {
XVariable var = xenv.getVar(name);
if(var.isExport() && var.getValue() != null &&
!var.getValue().isNull() && var.getValue().isAtomic())
env.put(name, var.getValue().toString());
}
}
// Special case for PATH and XPATH
XVariable vpath = xenv.getVar(ShellConstants.PATH);
if(vpath != null && vpath.isExport()) {
SearchPath p = new SearchPath(vpath.getValue());
String ps = p.toOSString();
env.put(ShellConstants.PATH, ps);
}
XVariable vxpath = xenv.getVar(ShellConstants.ENV_XPATH);
if(vxpath != null && vxpath.isExport()) {
SearchPath p = new SearchPath(vxpath.getValue());
String ps = p.toOSString();
env.put(ShellConstants.ENV_XPATH, ps);
}
}
/*
* (non-Javadoc)
*
* @see org.xmlsh.core.ICommand#getType()
*/
@Override
public CommandType getType() {
return CommandType.CMD_TYPE_EXTERNAL;
}
@Override
public SourceLocation getLocation() {
return mLocation;
}
@Override
public void setLocation(SourceLocation loc) {
mLocation = loc;
}
@Override
public void print(PrintWriter w, boolean bExec) {
w.print(mCommandFile.getPath());
}
@Override
public IModule getModule() {
return mModule;
}
}
//
//
// Copyright (C) 2008-2014 David A. Lee.
//
// The contents of this file are subject to the "Simplified BSD License" (the
// "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the
// License at http://www.opensource.org/licenses/bsd-license.php
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is David A. Lee
//
// Portions created by (your name) are Copyright (C) (your legal entity). All
// Rights Reserved.
//
// Contributor(s): none.
//