001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-24 22:47:39 +0000 (Mon, 24 Aug 2015) $' 
007 * '$Revision: 33633 $'
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 */
029
030package org.srb;
031
032// Ptolemy packages
033import java.io.BufferedReader;
034import java.io.DataInputStream;
035import java.io.File;
036import java.io.FileOutputStream;
037import java.io.IOException;
038import java.io.OutputStream;
039import java.io.StringReader;
040import java.util.Iterator;
041import java.util.List;
042
043import edu.sdsc.grid.io.srb.SRBFileSystem;
044import ptolemy.actor.IOPort;
045import ptolemy.actor.NoTokenException;
046import ptolemy.actor.TypedAtomicActor;
047import ptolemy.actor.TypedIOPort;
048import ptolemy.data.BooleanToken;
049import ptolemy.data.ObjectToken;
050import ptolemy.data.StringToken;
051import ptolemy.data.expr.FileParameter;
052import ptolemy.data.expr.Parameter;
053import ptolemy.data.expr.SingletonParameter;
054import ptolemy.data.expr.StringParameter;
055import ptolemy.data.type.BaseType;
056import ptolemy.kernel.CompositeEntity;
057import ptolemy.kernel.util.Attribute;
058import ptolemy.kernel.util.IllegalActionException;
059import ptolemy.kernel.util.NameDuplicationException;
060import ptolemy.util.MessageHandler;
061
062//////////////////////////////////////////////////////////////////////////
063//// SRBProxyCommand
064/**
065 * <p>
066 * SProxyCommand is a Kepler Actor which has a functionality similar to the SRB
067 * command namely "Spcommand". Spcommand performs the remote execution of
068 * arbitrary commands (executables) installed in a specific predefined directory
069 * on the remote host. SProxyCommand actor executes proxy commands on the SRB.
070 * The following actor expects as input a reference to the SRB file system. This
071 * reference connection is obtained via the SRBConnect Actor in Kepler. <i>See
072 * SRBConnect and its documentation.</i>
073 * </p>
074 * <p>
075 * The file reference system is created with a unique SRB user account and with
076 * this connection reference as input the SProxyCommand actor is able to gain
077 * access to the SRB file space. Once an alive SRB file connection system has
078 * been established the actor gets the command port and parameters to be
079 * executed as input from the user.The arguments to the command are take in as
080 * input and implemented as a multi/input port to support more than one
081 * argument. It concatanates the inputs in all the channels.
082 * </p>
083 * <p>
084 * <B>Actor Input:</B> Accepts a reference to the SRB files system, command and
085 * arguments to be executed as well as the outputfile name (optional)
086 * </p>
087 * <p>
088 * <B>Actor Output:</B> Outputs the result of execution of the proxy command as
089 * well as an outputfilehandle (if the outfile path exists)
090 * 
091 * </p>
092 * <p>
093 * The following actor accesses SRB file reference system and SRB file space
094 * with the SRB Jargon API provided. The JARGON is a pure API for developing
095 * programs with a data grid interface and I/O for SRB file systems.
096 * </p>
097 * <A href="http://www.sdsc.edu/srb"><I>Further information on SRB</I> </A>
098 * 
099 * 
100 @author Efrat Jaeger
101 * @version $Id: SRBProxyCommand.java 33633 2015-08-24 22:47:39Z crawl $
102 */
103
104public class SRBProxyCommand extends TypedAtomicActor {
105
106        /**
107         * Construct an actor with the given container and name.
108         * 
109         * @param container
110         *            The container.
111         * @param name
112         *            The name of this actor.
113         * @exception IllegalActionException
114         *                If the actor cannot be contained by the proposed
115         *                container.
116         * @exception NameDuplicationException
117         *                If the container already has an actor with this name.
118         */
119        public SRBProxyCommand(CompositeEntity container, String name)
120                        throws IllegalActionException, NameDuplicationException {
121                super(container, name);
122
123                SRBFileSystem = new TypedIOPort(this, "SRBFileSystem", true, false);
124                SRBFileSystem.setTypeEquals(BaseType.GENERAL);
125                new Attribute(SRBFileSystem, "_showName");
126
127                commandParameter = new StringParameter(this, "commandParameter");
128                commandParameter.setExpression("Please type your command here...");
129
130                command = new TypedIOPort(this, "command", true, false);
131                command.setTypeEquals(BaseType.STRING);
132                new Attribute(command, "_showName");
133
134                outputFile = new FileParameter(this, "outputFile");
135                // Construct input ports.
136                arguments = new TypedIOPort(this, "arguments", true, false);
137                arguments.setMultiport(true);
138                arguments.setTypeEquals(BaseType.STRING);
139                new Attribute(arguments, "_showName");
140
141                outputFileName = new TypedIOPort(this, "outputFileName", true, false);
142                outputFileName.setTypeEquals(BaseType.STRING);
143                new Attribute(outputFileName, "_showName");
144
145                output = new TypedIOPort(this, "output", false, true);
146                output.setTypeEquals(BaseType.STRING);
147                new Attribute(output, "_showName");
148
149                outfileHandle = new TypedIOPort(this, "outfileHandle", false, true);
150                outfileHandle.setTypeEquals(BaseType.STRING);
151                new Attribute(outfileHandle, "_showName");
152
153                outputLineByLine = new Parameter(this, "outputLineByLine",
154                                new BooleanToken(false));
155                outputLineByLine.setTypeEquals(BaseType.BOOLEAN);
156
157                hasTrigger = new Parameter(this, "hasTrigger", new BooleanToken(false));
158                hasTrigger.setTypeEquals(BaseType.BOOLEAN);
159
160                trigger = new TypedIOPort(this, "trigger", true, false);
161                hide = new SingletonParameter(trigger, "_hide");
162                hide.setToken(BooleanToken.TRUE);
163
164                _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" "
165                                + "width=\"60\" height=\"40\" " + "style=\"fill:white\"/>\n"
166                                + "<text x=\"4\" y=\"25\" "
167                                + "style=\"font-size:16; fill:blue; font-family:SansSerif\">"
168                                + "[SRB]</text>\n" + "<text x=\"45\" y=\"27\" "
169                                + "style=\"font-size:20; fill:blue; font-family:SansSerif\">"
170                                + "$</text>\n" + "</svg>\n");
171
172        } // constructor
173
174        // //////////////// Public ports and parameters ///////////////////////
175
176        public SingletonParameter hide;
177
178        /**
179         * pointer to the SRB file system.
180         */
181        public TypedIOPort SRBFileSystem;
182
183        /**
184         * command port to be executed.
185         */
186        public TypedIOPort command;
187
188        /**
189         * command parameter to be executed.
190         */
191        public StringParameter commandParameter;
192
193        /**
194         * Filled in if the user wants the command to output to a file.
195         */
196        public FileParameter outputFile;
197
198        /**
199         * The output file name is set by previous processes.
200         */
201        public TypedIOPort outputFileName;
202
203        /**
204         * The arguments to the command. Implemented as a multi/input port to
205         * support more than one argument. It concatanates the inputs in all the
206         * channels.
207         */
208        public TypedIOPort arguments;
209
210        /**
211         * The trigger port.
212         */
213        public TypedIOPort trigger;
214
215        /**
216         * The output file path, if exists.
217         */
218        public TypedIOPort outfileHandle;
219        /**
220         * The result stream of the command.
221         */
222        public TypedIOPort output;
223        // ** exitCode will be 1 if the command executes successfully.
224        // */
225        // public TypedIOPort exitCode;
226        /**
227         * If selected, broadcasts the output of the command line by line.
228         */
229        public Parameter outputLineByLine;
230
231        /**
232         * Unhide the trigger port when this parameter is true. This Parameter is
233         * type of boolean. NOTE: in fact, user can use the port configuration
234         * window to hide or unhide a port. This paremeter is here to provide a more
235         * intuitive interface for this actor.
236         */
237        public Parameter hasTrigger;
238
239        // /////////////////////////////////////////////////////////////////
240        // // public methods ////
241
242        /**
243         * If the specified attribute is <i>showTriggerPort</i>, then get the value
244         * of it and re-render the trigger port. If it is true, show the trigger
245         * port; if it is false, hide the trigger port.
246         * 
247         * @param attribute
248         *            The attribute that has changed.
249         * @exception IllegalActionException.
250         */
251        public void attributeChanged(Attribute attribute)
252                        throws IllegalActionException {
253                if (attribute == hasTrigger) {
254                        _triggerFlag = ((BooleanToken) hasTrigger.getToken())
255                                        .booleanValue();
256                        _debug("<TRIGGER_FLAG>" + _triggerFlag + "</TRIGGER_FLAG>");
257                        if (_triggerFlag) {
258                                hide.setToken(BooleanToken.FALSE);
259                        } else {
260                                List inPortList = this.inputPortList();
261                                Iterator ports = inPortList.iterator();
262                                while (ports.hasNext()) {
263                                        IOPort p = (IOPort) ports.next();
264                                        if (p.isInput()) {
265                                                try {
266                                                        if (p.getName().equals("trigger")) {
267                                                                // new Attribute(trigger, "_hideName");
268                                                                // p.setContainer(null);
269                                                                hide.setToken(BooleanToken.TRUE);
270                                                        }
271                                                } catch (Exception e) {
272                                                        throw new IllegalActionException(this, e
273                                                                        .getMessage());
274                                                }
275                                        }
276                                }// while
277                        }// else
278                } else {
279                        super.attributeChanged(attribute);
280                }
281        }
282
283        /**
284         * Sends a proxy command to be executed on SRB.
285         * 
286         * @exception IllegalActionException
287         *                If there is no director.
288         */
289        public void fire() throws IllegalActionException {
290
291                if (_triggerFlag) {
292                        List inPortList = this.inputPortList();
293                        Iterator ports = inPortList.iterator();
294                        while (ports.hasNext()) {
295                                IOPort p = (IOPort) ports.next();
296                                if (p.getName().equals("trigger")) {
297                                        if (p.getWidth() > 0) {
298                                                for (int i = 0; i < p.getWidth(); i++) {
299                                                        p.get(0);
300                                                }
301                                        }
302                                }
303                        }
304                }
305                // make sure there is an alive connection.
306                try {
307                        srbFileSystem.getHost();
308                } catch (Exception ex) { // connection was closed.
309                        srbFileSystem = null;
310                        ObjectToken SRBConOT = null;
311                        try { // try to get a new connection in case the previous one has
312                                        // terminated.
313                                SRBConOT = (ObjectToken) SRBFileSystem.get(0);
314                        } catch (NoTokenException ntex) {
315                        }
316                        if (SRBConOT != null) {
317                                srbFileSystem = (SRBFileSystem) SRBConOT.getValue();
318                        }
319                }
320                if (srbFileSystem == null) {
321                        throw new IllegalActionException(this,
322                                        "No SRB connection available in actor " + this.getName()
323                                                        + ".");
324                }
325
326                _lineFlag = ((BooleanToken) outputLineByLine.getToken()).booleanValue();
327                _debug("<TRIGGER_FLAG>" + _lineFlag + "</TRIGGER_FLAG>");
328
329                if (command.getWidth() > 0) {
330                        commandParameter.setExpression(((StringToken) command.get(0))
331                                        .stringValue());
332                }
333
334                _commandStr = ((StringToken) commandParameter.getToken()).stringValue();
335
336                String argString = "";
337                int i = 0;
338                int width = arguments.getWidth();
339                for (i = 0; i < width; i++) {
340                        if (arguments.hasToken(i)) {
341                                String argument = ((StringToken) arguments.get(i))
342                                                .stringValue();
343                                _debug("arguments(i) = " + argument);
344
345                                while (argument.indexOf("\\\"") != -1) {
346                                        int ind = argument.indexOf("\\\"");
347                                        argument = argument.substring(0, ind)
348                                                        + argument.substring(ind + 1, argument.length());
349                                        _debug(argument);
350                                }
351                                argString += argument + " ";
352                                _debug("argString = " + argString);
353                        }
354                }
355
356                StringBuffer outBuff = new StringBuffer("");
357                DataInputStream inStream = null;
358                OutputStream out = null;
359                byte[] bytesRead = new byte[20000];
360                ;
361                int nBytesRead;
362                String outFilePath = "";
363                try {
364                        if (outputFileName.getWidth() > 0) {
365                                String outFileName = ((StringToken) outputFileName.get(0))
366                                                .stringValue();
367                                outputFile.setExpression(outFileName);
368                        }
369                        // opening output file stream
370                        if (!outputFile.getExpression().equals("")) {
371                                outFilePath = outputFile.asURL().toString();
372                                File outFile = outputFile.asFile();
373                                File parent = outFile.getParentFile();
374                                if (!parent.exists()) {
375                                        if (!MessageHandler.yesNoQuestion("OK to create directory "
376                                                        + parent.getAbsolutePath() + "?")) {
377                                                srbFileSystem = SRBUtil.closeConnection(srbFileSystem);
378                                                throw new IllegalActionException(this,
379                                                                "Please select another output directory name.");
380                                        }
381                                }
382
383                                parent.mkdirs();
384                                if (outFile.exists()) {
385                                        if (!MessageHandler.yesNoQuestion("OK to overwrite "
386                                                        + outFilePath + "?")) {
387                                                srbFileSystem = SRBUtil.closeConnection(srbFileSystem);
388                                                throw new IllegalActionException(this,
389                                                                "Please select another output file name.");
390                                        }
391                                }
392
393                                out = new FileOutputStream(outFile);
394                        }
395
396                        // Executing the proxy command.
397                        try {
398                                inStream = (DataInputStream) srbFileSystem.executeProxyCommand(
399                                                _commandStr, argString);
400                        } catch (IOException ioex) {
401                                srbFileSystem = SRBUtil.closeConnection(srbFileSystem);
402                                ioex.printStackTrace();
403                                throw new IllegalActionException(this,
404                                                "Failed to execute SRB proxy command " + _commandStr
405                                                                + "in actor " + this.getName() + ": "
406                                                                + ioex.getMessage() + ".");
407                        }
408
409                        // processing the result.
410                        nBytesRead = inStream.read(bytesRead);
411                        while (nBytesRead > 0) {
412                                // if there is a specified file, write to it.
413                                if (out != null) {
414                                        out.write(bytesRead, 0, nBytesRead);
415                                }
416                                // append binary result to a string buffer.
417                                outBuff.append(new String(bytesRead, 0, nBytesRead));
418                                nBytesRead = inStream.read(bytesRead);
419                        }
420                        if (out != null)
421                                out.close();
422
423                        // process string result.
424                        if (_lineFlag) { // output each line separately.
425                                BufferedReader br = new BufferedReader(new StringReader(outBuff
426                                                .toString()));
427                                String line;
428                                while ((line = br.readLine()) != null) {
429                                        output.broadcast(new StringToken(line));
430                                }
431                                // output the whole result string at once.
432                        } else {
433                                output.broadcast(new StringToken(outBuff.toString()));
434                        }
435                } catch (IOException ioe) {
436                        srbFileSystem = SRBUtil.closeConnection(srbFileSystem);
437                        _debug("<IOException> when reading the input: " + ioe
438                                        + "</IOException>");
439                        throw new IllegalActionException(this,
440                                        "IOException when reading the input: " + ioe);
441                }
442
443                // output out file handle if exists.
444                if (!outFilePath.equals("")) {
445                        outfileHandle.broadcast(new StringToken(outFilePath));
446                }
447        }
448
449        /**
450         * Initialize the srb file system to null.
451         */
452        public void initialize() throws IllegalActionException {
453                super.initialize();
454                srbFileSystem = null;
455        }
456
457        /**
458         * Disconnect from SRB.
459         */
460        public void wrapup() {
461                srbFileSystem = SRBUtil.closeConnection(srbFileSystem);
462        }
463
464        // ////////////////////////////////////////////////////////////////////
465        // // private variables ////
466
467        /**
468         * Command to be executed.
469         */
470        private String _commandStr = "";
471
472        /**
473         * Indicator to output each line separately.
474         */
475        private boolean _lineFlag = false;
476
477        /**
478         * Has trigger indicator
479         */
480        private boolean _triggerFlag = false;
481        // private int _charsToSkip = 6;
482
483        /**
484         * An srb file system variable.
485         */
486        private SRBFileSystem srbFileSystem = null;
487}