001/*
002 * Copyright (c) 2002-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 */
029
030package org.geon;
031
032import java.io.File;
033import java.io.FileInputStream;
034import java.io.FileOutputStream;
035import java.io.IOException;
036
037import org.apache.commons.net.ftp.FTP;
038
039import ptolemy.actor.TypedAtomicActor;
040import ptolemy.actor.TypedIOPort;
041import ptolemy.data.StringToken;
042import ptolemy.data.Token;
043import ptolemy.data.expr.StringParameter;
044import ptolemy.data.type.BaseType;
045import ptolemy.kernel.CompositeEntity;
046import ptolemy.kernel.util.Attribute;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.NameDuplicationException;
049import ptolemy.kernel.util.StringAttribute;
050
051//////////////////////////////////////////////////////////////////////////
052//// FTPClient
053/**
054 * This actor serves as an interface for FTP operations (currently only upload
055 * and download are supported).
056 */
057
058public class FTPClient extends TypedAtomicActor {
059
060        public FTPClient(CompositeEntity container, String name)
061                        throws IllegalActionException, NameDuplicationException {
062                super(container, name);
063
064                arguments = new TypedIOPort(this, "arguments", true, false);
065                arguments.setMultiport(true);
066                arguments.setTypeEquals(BaseType.STRING);
067
068                // trigger = new TypedIOPort(this, "trigger", false, true);
069                // trigger.setTypeEquals(BaseType.STRING);
070
071                url = new TypedIOPort(this, "url", false, true);
072                url.setTypeEquals(BaseType.STRING);
073
074                operation = new StringParameter(this, "operation");
075                operation.setExpression("GET");
076                operation.addChoice("PUT");
077                mode = new StringParameter(this, "mode");
078                mode.setExpression("ASC");
079                mode.addChoice("BIN");
080
081                host = new StringAttribute(this, "host");
082                remotePath = new StringAttribute(this, "remote path");
083                username = new StringAttribute(this, "username");
084                password = new StringAttribute(this, "password");
085                localPath = new StringParameter(this, "localPath");
086                localPath.setExpression(System.getProperty("user.dir"));
087
088                _attachText("_iconDescription", "<svg>\n"
089                                + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
090                                + "style=\"fill:white\"/>\n"
091                                + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
092                                + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n"
093                                + "</svg>\n");
094        }
095
096        // /////////////////////////////////////////////////////////////////
097        // // ports and parameters ////
098        /**
099         * The input port, which is a multiport.
100         */
101        // public TypedIOPort trigger;
102        /** Source or destination files to be uploaded/downloaded. */
103        public TypedIOPort arguments;
104
105        /** URL of the uploaded/downloaded file. */
106        public TypedIOPort url;
107
108        /** Operation performed: put/get */
109        public StringParameter operation;
110
111        /** Transfer mode: asc/bin */
112        public StringParameter mode;
113
114        /** host server name. */
115        public StringAttribute host;
116
117        /** path to remote file (begins and ends with '/'). */
118        public StringAttribute remotePath;
119
120        /** Authentication username */
121        public StringAttribute username;
122
123        /** Authentication password */
124        public StringAttribute password;
125        
126        /** Local directory */
127        public StringParameter localPath;
128
129        // /////////////////////////////////////////////////////////////////
130        // // public methods ////
131
132        /**
133         * If the specified attribute is <i>fileOrURL</i> and there is an open file
134         * being read, then close that file and open the new one; if the attribute
135         * is <i>numberOfLinesToSkip</i> and its value is negative, then throw an
136         * exception. In the case of <i>fileOrURL</i>, do nothing if the file name
137         * is the same as the previous value of this attribute.
138         * 
139         * @param attribute
140         *            The attribute that has changed.
141         * @exception IllegalActionException
142         *                If the specified attribute is <i>fileOrURL</i> and the
143         *                file cannot be opened, or the previously opened file
144         *                cannot be closed; or if the attribute is
145         *                <i>numberOfLinesToSkip</i> and its value is negative.
146         */
147
148        /**
149         * Determine the output format
150         * 
151         * @param attribute
152         *            The attribute that changed.
153         * @exception IllegalActionException
154         *                If the output type is not recognized.
155         */
156        public void attributeChanged(Attribute attribute)
157                        throws IllegalActionException {
158                try {
159                        if (attribute == operation) {
160                                String strOperation = operation.getExpression();
161                                if (strOperation.equals("GET")) {
162                                        _operation = _GET;
163                                } else if (strOperation.equals("PUT")) {
164                                        _operation = _PUT;
165                                } else {
166                                        throw new IllegalActionException(this,
167                                                        "Unrecognized operation function: " + strOperation);
168                                }
169                        } else if (attribute == mode) {
170                                String strMode = mode.getExpression();
171                                if (strMode.equals("ASC")) {
172                                        _mode = _ASC;
173                                } else if (strMode.equals("BIN")) {
174                                        _mode = _BIN;
175                                } else {
176                                        throw new IllegalActionException(this,
177                                                        "Unrecognized mode function: " + strMode);
178                                }
179                        } else {
180                                super.attributeChanged(attribute);
181                        }
182                } catch (Exception nameDuplication) {
183                        /*
184                         * throw new InternalErrorException(this, nameDuplication,
185                         * "Unexpected name duplication");
186                         */
187                }
188        }
189
190        /**
191         * Output the data lines into an array.
192         * 
193         * @exception IllegalActionException
194         *                If there's no director.
195         */
196
197        public void fire() throws IllegalActionException {
198                super.fire();
199
200                String _host = host.getExpression();
201                String _remotePath = remotePath.getExpression();
202                String _username = username.getExpression();
203                String _password = password.getExpression();
204                
205                String localPathStr = System.getProperty("user.dir");
206                Token token = localPath.getToken();
207                if(token != null) {
208                    localPathStr = ((StringToken)token).stringValue();
209                }
210
211                String failMsg = "FTP transfer failed because of: ";
212                try {
213                        org.apache.commons.net.ftp.FTPClient f = new org.apache.commons.net.ftp.FTPClient();
214                        f.connect(_host);
215                        f.login(_username, _password);
216                        f.changeWorkingDirectory(_remotePath);
217                    f.cwd(localPathStr);
218                        if (_mode == _BIN) {
219                                f.setFileType(FTP.BINARY_FILE_TYPE);
220                        }
221
222                        // for all channels get input...
223                        int argsWidth = arguments.getWidth();
224                        for (int i = 0; i < argsWidth; i++) {
225                                String _arg = ((StringToken) arguments.get(i)).stringValue();
226                                _debug("argument(" + i + ") = " + _arg);
227                                File argFile = new File(localPathStr, _arg);
228                                _debug("file exist?  " + argFile.exists());
229                                // extract filename
230                                _debug("_remotePath = " + _remotePath + argFile.getName());
231                                if (_operation == _GET) {
232                                        FileOutputStream os = new FileOutputStream(argFile);
233                                        f.retrieveFile(argFile.getName(), os);
234                                        os.close();
235                                } else if (_operation == _PUT && argFile.exists()) {
236                                        // TODO: add if get fails then put.
237                                        FileInputStream is = new FileInputStream(argFile);
238                                        f.storeFile(argFile.getName(), is);
239                                        is.close();
240                                } else {
241                                        _url = "invalid command";
242                                }
243                        }
244                        f.disconnect();
245                } catch (IOException ioe) {
246                        throw new IllegalActionException(failMsg + ioe.toString());
247                }
248                url.broadcast(new StringToken(_url));
249        }
250
251        /**
252         * Post fire the actor. Return false to indicated that the process has
253         * finished. If it returns true, the process will continue indefinitely.
254         */
255
256        public boolean postfire() {
257                return false;
258        }
259
260        // /////////////////////////////////////////////////////////////////
261        // // private variables ////
262        /**
263         * Output indicator parameter.
264         */
265        private int _operation;
266        private int _mode;
267
268        // Constants used for more efficient execution.
269        private static final int _GET = 0;
270        private static final int _PUT = 1;
271
272        private static final int _ASC = 0;
273        private static final int _BIN = 1;
274
275        /** Result string Variable. */
276        private String _url = new String("");
277
278}