001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-11-26 22:19:36 +0000 (Mon, 26 Nov 2012) $'
007 * '$Revision: 31113 $'
008 *
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029package org.kepler.actor.gsissh;
030
031import java.io.ByteArrayOutputStream;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.kepler.ssh.ExecException;
036import org.kepler.ssh.ExecFactory;
037import org.kepler.ssh.ExecInterface;
038
039import ptolemy.actor.TypedAtomicActor;
040import ptolemy.actor.TypedIOPort;
041import ptolemy.actor.parameters.PortParameter;
042import ptolemy.data.BooleanToken;
043import ptolemy.data.IntToken;
044import ptolemy.data.StringToken;
045import ptolemy.data.expr.Parameter;
046import ptolemy.data.type.BaseType;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050
051/**
052* Connects to a remote host using gsissh and execute commands.
053* It also provides functionalities to copy files between two
054* machines using sftp. Currently doesn't support recursive copy.
055* <p>
056* If the <i>target</i> is empty string or equals <b>local</b>, the Java Runtime
057* will be used for local execution instead of ssh. It behaves similarly to
058* other local command-line exec actors of Kepler but you do not need to change
059* your workflow for remote/local executions by using this actor.
060* <p>
061* This actor works similar to ExecuteCmd( org.kepler.actor.ssh.ExecuteCmd )
062* but additionally supports connecting to grid server using grid certificates
063*
064* @author Chandrika Sivaramakrishnan
065*
066*/
067public class ExecGsisshCmd extends TypedAtomicActor {
068        private static Log log = LogFactory.getLog(ExecGsisshCmd.class);
069        boolean isDebugging = log.isDebugEnabled();
070
071        public ExecGsisshCmd(CompositeEntity container, String name)
072                        throws NameDuplicationException, IllegalActionException {
073                super(container, name);
074
075                // target selects the machine where to connect to
076                target = new PortParameter(this, "target", new StringToken(
077                                "[user@]host[:port]"));
078                new Parameter(target.getPort(), "_showName", BooleanToken.TRUE);
079
080                command = new TypedIOPort(this, "command", true, false);
081                command.setTypeEquals(BaseType.STRING);
082                new Parameter(command, "_showName", BooleanToken.TRUE);
083
084                stdout = new TypedIOPort(this, "stdout", false, true);
085                stdout.setTypeEquals(BaseType.STRING);
086                new Parameter(stdout, "_showName", BooleanToken.TRUE);
087
088                stderr = new TypedIOPort(this, "stderr", false, true);
089                stderr.setTypeEquals(BaseType.STRING);
090                new Parameter(stderr, "_showName", BooleanToken.TRUE);
091
092                exitcode = new TypedIOPort(this, "exitcode", false, true);
093                exitcode.setTypeEquals(BaseType.INT);
094                new Parameter(exitcode, "_showName", BooleanToken.TRUE);
095
096                errors = new TypedIOPort(this, "errors", false, true);
097                errors.setTypeEquals(BaseType.STRING);
098                new Parameter(errors, "_showName", BooleanToken.TRUE);
099
100                timeoutSeconds = new Parameter(this, "timeoutSeconds", new IntToken(0));
101                timeoutSeconds.setTypeEquals(BaseType.INT);
102
103                cleanupAfterError = new Parameter(this, "cleanupAfterError",
104                                new BooleanToken(false));
105                cleanupAfterError.setTypeEquals(BaseType.BOOLEAN);
106
107                thirdParty = new PortParameter(this, "thirdParty", new StringToken(""));
108                new Parameter(thirdParty.getPort(), "_showName", BooleanToken.TRUE);
109
110                _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" "
111                                + "width=\"75\" height=\"50\" style=\"fill:blue\"/>\n"
112                                + "<text x=\"5\" y=\"30\""
113                                + "style=\"font-size:14; fill:yellow; font-family:SansSerif\">"
114                                + "ExecCmd</text>\n" + "</svg>\n");
115        }
116        // //////////////// Public ports and parameters ///////////////////////
117
118        /**
119         * Target in user@host:port format. If user is not provided, the local
120         * username will be used. If port is not provided, the default port 22 will
121         * be applied. If target is "local" or empty string, the command will be
122         * executed locally, using Java Runtime.
123         */
124        public PortParameter target;
125
126        /**
127         * The command to be executed on the remote host. It needs to be provided as
128         * a string.
129         */
130        public TypedIOPort command;
131
132        /**
133         * Third party target in user@host:port format. If user is not provided, the
134         * local username will be used. If port is not provided, the default port 22
135         * will be applied.
136         */
137        public PortParameter thirdParty;
138
139        /**
140         * Output of the command as it would output to the standard shell output.
141         */
142        public TypedIOPort stdout;
143
144        /**
145         * The error that were reported by the remote execution or while connecting.
146         */
147        public TypedIOPort stderr;
148
149        /**
150         * The exit code of the command.
151         */
152        public TypedIOPort exitcode;
153
154        /**
155         * The string representation of all the errors that happened during the
156         * execution of the actor, if there are any.
157         */
158        public TypedIOPort errors;
159
160        /**
161         * Timeout in seconds for the command to be executed. 0 means waiting
162         * indefinitely for command termination.
163         */
164        public Parameter timeoutSeconds;
165
166        /**
167         * Enforce killing remote process(es) after an error or timeout. Unix
168         * specific solution is used, therefore you should not set this flag if
169         * connecting to other servers. But it is very useful for unix as timeout
170         * leaves processes living there, and sometimes errors too. All processes
171         * belonging to the same group as the remote command (i.e. its children)
172         * will be killed.
173         */
174        public Parameter cleanupAfterError;
175
176
177        ///////////////////////////////////////////////////////////////////
178        //// public methods ////
179
180        /**
181         * Send the token in the <i>value</i> parameter to the output.
182         *
183         * @exception IllegalActionException
184         *                If it is thrown by the send() method sending out the
185         *                token.
186         */
187        public void fire() throws IllegalActionException {
188                super.fire();
189
190                // process inputs
191                target.update();
192                StringToken tg = (StringToken) target.getToken();
193                String strTarget = tg.stringValue();
194                String strCommand = ((StringToken) command.get(0)).stringValue();
195                int timeout = ((IntToken) timeoutSeconds.getToken()).intValue();
196                boolean cleanup = ((BooleanToken) cleanupAfterError.getToken())
197                                .booleanValue();
198
199                // third party target
200                thirdParty.update();
201                String strThirdParty = ((StringToken) thirdParty.getToken())
202                                .stringValue();
203
204                // get the execution object
205                //ExecInterface execObj = ExecFactory.getExecObject(strTarget);
206                //ExecInterface execObj = new GsiSshExec(strTarget);
207                ExecInterface execObj;
208                try {
209                        execObj = ExecFactory.getExecObject(strTarget);
210                } catch (ExecException e1) {
211                        String errText = new String("ExecuteCmd error:\n" + e1.getMessage());
212                        log.error(errText);
213                        stdout.send(0, new StringToken(""));
214                        stderr.send(0, new StringToken(""));
215                        exitcode.send(0, new IntToken(-32767));
216                        errors.send(0, new StringToken(errText));
217                        return;
218                }
219                execObj.setTimeout(timeout, false, false);
220                execObj.setForcedCleanUp(cleanup);
221
222                int exitCode = 0;
223                ByteArrayOutputStream cmdStdout = new ByteArrayOutputStream();
224                ByteArrayOutputStream cmdStderr = new ByteArrayOutputStream();
225
226                // execute command
227                try {
228                        log.info("Exec cmd: " + strCommand);
229                        exitCode = execObj.executeCmd(strCommand, cmdStdout, cmdStderr,
230                                        strThirdParty);
231
232                } catch (ExecException e) {
233                        String errText = new String("ExecuteCmd error:\n" + e.getMessage());
234                        log.error(errText);
235                        stdout.send(0, new StringToken(""));
236                        stderr.send(0, new StringToken(""));
237                        exitcode.send(0, new IntToken(-32767));
238                        errors.send(0, new StringToken(errText));
239                        return;
240                }
241
242                if (isDebugging)
243                        log.debug("exit code = " + exitCode);
244
245                System.out.println("From cmdStderr="+cmdStderr.toString());
246                System.out.println("From cmdStdout="+cmdStdout.toString());
247                // send stdout, stderr and empty string as internal errors
248                exitcode.send(0, new IntToken(exitCode));
249                stdout.send(0, new StringToken(cmdStdout.toString()));
250                stderr.send(0, new StringToken(cmdStderr.toString()));
251                errors.send(0, new StringToken(""));
252
253        } // end-method fire()
254
255}