/** * $Id: $ * $Date: $ * */ package org.xmlsh.sh.ui; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.OutputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import javax.swing.JButton; import javax.swing.JTextField; import javax.swing.SwingUtilities; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xmlsh.core.ThrowException; import org.xmlsh.core.XValue; import org.xmlsh.core.io.StreamInputPort; import org.xmlsh.sh.core.IExpression; import org.xmlsh.sh.core.SourceLocator; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.sh.shell.Shell; import org.xmlsh.sh.shell.ShellConstants; import org.xmlsh.util.Util; // Use a different name to avoid confusion with sh.shell.ShellThread public class XShellThread extends Thread { private volatile Shell mShell = null; private boolean mClosed = false ; private TextResultPane mResultTextArea; private JButton mStopButton; private JButton mStartButton; private BlockingQueue<String> mCommandQueue = new ArrayBlockingQueue<String>(2, true); private OutputStream mResultOutputStream; private OutputStream mResultErrorStream; private List<XValue> mArgs; private JTextField mCommandField; private TextFieldStreamPipe cmdPipe ; private SerializeOpts mSerializeOpts; private XShell mXShell; private static Logger mLogger = LogManager.getLogger(); private void print(String s) throws UnsupportedEncodingException, IOException { mResultErrorStream.write(s.getBytes("UTF8")); } public XShellThread(XShell xshell, ThreadGroup group , List<XValue> args, TextResultPane resultTextArea, JTextField commandField , JButton startButton , JButton stopButton, SerializeOpts serializeOpts) throws IOException { super(group , "xmlshui" ); mXShell = xshell ; mArgs = args ; mResultTextArea = resultTextArea; mStartButton = startButton ; mStopButton = stopButton ; mCommandField = commandField ; mSerializeOpts = serializeOpts; } private void setRunning(final boolean bRunning) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { mStartButton.setEnabled(!bRunning); mStopButton.setEnabled(bRunning); cmdPipe.setReading(bRunning); } }); } private void clearResult() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { mResultTextArea.clear(); } }); } @Override public void run() { final XShellThread thisThread = this ; mResultTextArea.clear(); IExpression c = null; mResultOutputStream = new TextComponentOutputStream(mResultTextArea, mSerializeOpts , "stdout"); mResultErrorStream = new TextComponentOutputStream(mResultTextArea, mSerializeOpts , "stderr"); mStopButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if( cmdPipe != null ) cmdPipe.reset(); if( mShell != null ) { if( thisThread != Thread.currentThread() ) { interrupt(); Thread.yield(); } mShell.shutdown(true,0); } } }); try { cmdPipe = new TextFieldStreamPipe( mCommandField , mSerializeOpts); setRunning(false); String sCmd = null ; while (! mClosed ){ if(( sCmd = mCommandQueue.take()) == null) break ; try ( StreamInputPort inp = new StreamInputPort(cmdPipe.getIn(), null) ){ setRunning(false); clearResult(); //@TODO: Use ShellIO mShell = new Shell(); mShell.setArgs(mArgs == null ? new ArrayList<XValue>() : mArgs ); mShell.setArg0("xmlshui"); mShell.getSerializeOpts().setInputTextEncoding(ShellConstants.kENCODING_UTF_8); mShell.getSerializeOpts().setOutputTextEncoding(ShellConstants.kENCODING_UTF_8); mShell.getEnv().setStdout(mResultOutputStream); mShell.getEnv().setStderr(mResultErrorStream); // setInput will create a reference mShell.getEnv().setInput(null, inp); setRunning(true); try ( Reader reader = Util.toReader(sCmd ) ){ mShell.runScript(reader , "xmlshui", true ); mResultOutputStream.flush(); mResultErrorStream.flush(); setRunning(false); } catch (Exception e) { mLogger.info("Exception running shell commands",e); print(e.getMessage()); } } catch (ThrowException e) { mLogger.info("Throw running shell commands",e); print("Ignoring thrown value: " + e.getMessage()); } catch (Exception e) { mLogger .warn("Exception running shell commands",e); SourceLocator loc = c != null ? c.getSourceLocation() : null; if (loc != null) { String sLoc = loc.toString(); print(sLoc); } print(e.getMessage()); } catch (Error e) { mLogger.info("Error running shell commands",e); print("Error: " + e.getMessage()); SourceLocator loc = c != null ? c.getSourceLocation() : null; if (loc != null) { String sLoc = loc.toString(); print(sLoc); } } finally { if( mShell != null ) { mShell.shutdown(true,1000); setRunning(false); mShell = null ; } if( cmdPipe != null) cmdPipe.close(); cmdPipe = null ; cmdPipe = new TextFieldStreamPipe( mCommandField , mSerializeOpts); } } } catch( Exception e ) { mLogger .warn("Exception running shell commands",e); }finally { if( mShell != null ) { mShell.shutdown(true,100); } Util.safeClose( cmdPipe ); if( mXShell != null ) mXShell.onThreadExit( this ); mXShell = null ; } } private synchronized void printError(Exception e) { try { mResultErrorStream.flush(); } catch (IOException e1) { mLogger .warn("Exception running shell commands",e); } } /** * @return the blockingQueue * @throws InterruptedException * * Called by the AWT event thread */ boolean putCommand(String command) { try { mCommandQueue.put(command); } catch (InterruptedException e) { mLogger .warn("Exception running shell commands",e); printError(e); return false; } return true; } public synchronized void close() { mClosed = true ; interrupt(); if( mShell != null ) { mShell.shutdown(true,100); } } } // // // 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. //