Save This Page
Home » j2ssh-0.2.9-src » com.sshtools.ant » [javadoc | source]
    1   /*
    2    *  SSHTools - Java SSH2 API
    3    *
    4    *  Copyright (C) 2002-2003 Lee David Painter and Contributors.
    5    *
    6    *  Contributions made by:
    7    *
    8    *  Brett Smith
    9    *  Richard Pernavas
   10    *  Erwin Bolwidt
   11    *
   12    *  This program is free software; you can redistribute it and/or
   13    *  modify it under the terms of the GNU General Public License
   14    *  as published by the Free Software Foundation; either version 2
   15    *  of the License, or (at your option) any later version.
   16    *
   17    *  This program is distributed in the hope that it will be useful,
   18    *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   19    *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20    *  GNU General Public License for more details.
   21    *
   22    *  You should have received a copy of the GNU General Public License
   23    *  along with this program; if not, write to the Free Software
   24    *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   25    */
   26   package com.sshtools.ant;
   27   
   28   import com.sshtools.j2ssh;
   29   import com.sshtools.j2ssh.sftp;
   30   
   31   import org.apache.tools.ant;
   32   import org.apache.tools.ant.types;
   33   import org.apache.tools.ant.util;
   34   
   35   import java.io;
   36   
   37   import java.util;
   38   
   39   
   40   /**
   41    * Basic SFTP client. Performs the following actions:
   42    * <ul>
   43    *   <li> <strong>put</strong> - send files to a remote server. This is the
   44    *   default action.</li>
   45    *   <li> <strong>get</strong> - retreive files from a remote server.</li>
   46    *   <li> <strong>del</strong> - delete files from a remote server.</li>
   47    *   <li> <strong>chmod</strong> - changes the mode of files on a remote server.</li>
   48    * </ul>
   49    *
   50    */
   51   public class Sftp extends SshSubTask {
   52       protected static final int SEND_FILES = 0;
   53       protected static final int GET_FILES = 1;
   54       protected static final int DEL_FILES = 2;
   55       protected static final int MK_DIR = 4;
   56       protected static final int CHMOD = 5;
   57   
   58       //private Ssh parent = null;
   59       protected static final String[] ACTION_STRS = {
   60           "Sending", "Getting", "Deleting", "Listing", "Making directory", "chmod"
   61       };
   62       protected static final String[] COMPLETED_ACTION_STRS = {
   63           "Sent", "Retrieved", "Deleted", "Listed", "Created directory",
   64           "Mode changed"
   65       };
   66       private String remotedir = ".";
   67   
   68       //  private File listing;
   69       private boolean verbose = false;
   70       private boolean newerOnly = false;
   71       private int action = SEND_FILES;
   72       private Vector filesets = new Vector();
   73       private Vector dirCache = new Vector();
   74       private int transferred = 0;
   75       private String remoteFileSep = "/";
   76       private boolean skipFailedTransfers = false;
   77       private int skipped = 0;
   78       private boolean ignoreNoncriticalErrors = false;
   79       private String chmod = "777";
   80       private FileUtils fileUtils = FileUtils.newFileUtils();
   81   
   82       /**
   83        * Set to true to receive notification about each file as it is
   84        * transferred.
   85        */
   86       public void setVerbose(boolean verbose) {
   87           this.verbose = verbose;
   88       }
   89   
   90       /**
   91        * Sets the remote working directory
   92        * */
   93       public void setRemotedir(String remotedir) {
   94           this.remotedir = remotedir;
   95       }
   96   
   97       /**
   98            * A synonym for <tt>depends</tt>. Set to true to transmit only new or changed
   99        * files.
  100        */
  101       public void setNewer(boolean newer) {
  102           this.newerOnly = newer;
  103       }
  104   
  105       /**
  106        * Set to true to transmit only files that are new or changed from their
  107        * remote counterparts. The default is to transmit all files.
  108        */
  109       public void setDepends(boolean depends) {
  110           this.newerOnly = depends;
  111       }
  112   
  113       /**
  114        * Sets the file permission mode (Unix only) for files sent to the server.
  115        */
  116       public void setChmod(String theMode) {
  117           this.chmod = theMode;
  118       }
  119   
  120       /**
  121        *  A set of files to upload or download
  122        */
  123       public void addFileset(FileSet set) {
  124           filesets.addElement(set);
  125       }
  126   
  127       /**
  128        * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
  129        * "mkdir" and "list".
  130        *
  131        * @deprecated setAction(String) is deprecated and is replaced with
  132        *      setAction(FTP.Action) to make Ant's Introspection mechanism do the
  133        *      work and also to encapsulate operations on the type in its own
  134        *      class.
  135        * @ant.attribute ignore="true"
  136        */
  137   
  138       /* public void setAction(String action) throws BuildException {
  139          log("DEPRECATED - The setAction(String) method has been deprecated."
  140              + " Use setAction(FTP.Action) instead.");
  141          Action a = new Action();
  142          a.setValue(action);
  143          this.action = a.getAction();
  144        }*/
  145   
  146       /**
  147        * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
  148        * "mkdir", "chmod" and "list".
  149        */
  150       public void setAction(Action action) throws BuildException {
  151           this.action = action.getAction();
  152       }
  153   
  154       /**
  155        * If true, enables unsuccessful file put, delete and get
  156        * operations to be skipped with a warning and the remainder
  157        * of the files still transferred.
  158        */
  159       public void setSkipFailedTransfers(boolean skipFailedTransfers) {
  160           this.skipFailedTransfers = skipFailedTransfers;
  161       }
  162   
  163       /**
  164        * set the flag to skip errors on directory creation.
  165        * (and maybe later other server specific errors)
  166        */
  167       public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) {
  168           this.ignoreNoncriticalErrors = ignoreNoncriticalErrors;
  169       }
  170   
  171       /** Checks to see that all required parameters are set.  */
  172       protected void checkConfiguration() throws BuildException {
  173           /* if ( (action == LIST_FILES) && (listing == null)) {
  174              throw new BuildException("listing attribute must be set for list "
  175                                       + "action!");
  176            }*/
  177           if ((action == MK_DIR) && (remotedir == null)) {
  178               throw new BuildException("remotedir attribute must be set for " +
  179                   "mkdir action!");
  180           }
  181   
  182           if ((action == CHMOD) && (chmod == null)) {
  183               throw new BuildException("chmod attribute must be set for chmod " +
  184                   "action!");
  185           }
  186       }
  187   
  188       /**
  189        * For each file in the fileset, do the appropriate action: send, get,
  190        * delete, or list.
  191        */
  192       protected int transferFiles(SftpClient sftp, FileSet fs)
  193           throws IOException, BuildException {
  194           FileScanner ds;
  195   
  196           if (action == SEND_FILES) {
  197               ds = fs.getDirectoryScanner(parent.getProject());
  198           } else {
  199               ds = new SftpDirectoryScanner(sftp);
  200               fs.setupDirectoryScanner(ds, parent.getProject());
  201               ds.scan();
  202           }
  203   
  204           String[] dsfiles = ds.getIncludedFiles();
  205           String dir = null;
  206   
  207           if ((ds.getBasedir() == null) &&
  208                   ((action == SEND_FILES) || (action == GET_FILES))) {
  209               throw new BuildException("the dir attribute must be set for send " +
  210                   "and get actions");
  211           } else {
  212               if ((action == SEND_FILES) || (action == GET_FILES)) {
  213                   dir = ds.getBasedir().getAbsolutePath();
  214               }
  215           }
  216   
  217           // If we are doing a listing, we need the output stream created now.
  218           BufferedWriter bw = null;
  219   
  220           try {
  221               /*if (action == LIST_FILES) {
  222                 File pd = fileUtils.getParentFile(listing);
  223                 if (!pd.exists()) {
  224                   pd.mkdirs();
  225                 }
  226                 bw = new BufferedWriter(new FileWriter(listing));
  227                      }*/
  228               for (int i = 0; i < dsfiles.length; i++) {
  229                   switch (action) {
  230                   case SEND_FILES: {
  231                       sendFile(sftp, dir, dsfiles[i]);
  232   
  233                       break;
  234                   }
  235   
  236                   case GET_FILES: {
  237                       getFile(sftp, dir, dsfiles[i]);
  238   
  239                       break;
  240                   }
  241   
  242                   case DEL_FILES: {
  243                       delFile(sftp, dsfiles[i]);
  244   
  245                       break;
  246                   }
  247   
  248                   case CHMOD: {
  249                       chmod(sftp, dsfiles[i]);
  250                       transferred++;
  251   
  252                       break;
  253                   }
  254   
  255                   default:throw new BuildException("unknown ftp action " +
  256                           action);
  257                   }
  258               }
  259           } finally {
  260               if (bw != null) {
  261                   bw.close();
  262               }
  263           }
  264   
  265           return dsfiles.length;
  266       }
  267   
  268       /**
  269        * Sends all files specified by the configured filesets to the remote
  270        * server.
  271        */
  272       protected void transferFiles(SftpClient sftp)
  273           throws IOException, BuildException {
  274           transferred = 0;
  275           skipped = 0;
  276   
  277           if (filesets.size() == 0) {
  278               throw new BuildException("at least one fileset must be specified.");
  279           } else {
  280               // get files from filesets
  281               for (int i = 0; i < filesets.size(); i++) {
  282                   FileSet fs = (FileSet) filesets.elementAt(i);
  283   
  284                   if (fs != null) {
  285                       transferFiles(sftp, fs);
  286                   }
  287               }
  288           }
  289   
  290           log(transferred + " files " + COMPLETED_ACTION_STRS[action]);
  291   
  292           if (skipped != 0) {
  293               log(skipped + " files were not successfully " +
  294                   COMPLETED_ACTION_STRS[action]);
  295           }
  296       }
  297   
  298       /**
  299        * Correct a file path to correspond to the remote host requirements. This
  300        * implementation currently assumes that the remote end can handle
  301        * Unix-style paths with forward-slash separators. This can be overridden
  302        * with the <code>separator</code> task parameter. No attempt is made to
  303        * determine what syntax is appropriate for the remote host.
  304        */
  305       protected String resolveFile(String file) {
  306           return file.replace(System.getProperty("file.separator").charAt(0),
  307               remoteFileSep.charAt(0));
  308       }
  309   
  310       /**
  311        * Creates all parent directories specified in a complete relative
  312        * pathname. Attempts to create existing directories will not cause
  313        * errors.
  314        */
  315       protected void createParents(SftpClient sftp, String filename)
  316           throws IOException, BuildException {
  317           Vector parents = new Vector();
  318           File dir = new File(filename);
  319           String dirname;
  320   
  321           while ((dirname = dir.getParent()) != null) {
  322               dir = new File(dirname);
  323               parents.addElement(dir);
  324           }
  325   
  326           for (int i = parents.size() - 1; i >= 0; i--) {
  327               dir = (File) parents.elementAt(i);
  328   
  329               if (!dirCache.contains(dir)) {
  330                   log("creating remote directory " + resolveFile(dir.getPath()),
  331                       Project.MSG_VERBOSE);
  332   
  333                   try {
  334                       sftp.mkdir(resolveFile(dir.getPath()));
  335                   } catch (IOException ex) {
  336                   }
  337   
  338                   dirCache.addElement(dir);
  339               }
  340           }
  341       }
  342   
  343       /**
  344        * Checks to see if the remote file is current as compared with the local
  345        * file. Returns true if the remote file is up to date.
  346        */
  347       protected boolean isUpToDate(SftpClient sftp, File localFile,
  348           String remoteFile) throws IOException, BuildException {
  349           try {
  350               log("Checking date for " + remoteFile, Project.MSG_VERBOSE);
  351   
  352               FileAttributes attrs = sftp.stat(remoteFile);
  353   
  354               // SFTP uses seconds since Jan 1 1970 UTC
  355               long remoteTimestamp = attrs.getModifiedTime().longValue() * 1000; //files[0].getTimestamp().getTime().getTime();
  356   
  357               // Java uses milliseconds since Jan 1 1970 UTC
  358               long localTimestamp = localFile.lastModified();
  359   
  360               if (this.action == SEND_FILES) {
  361                   return remoteTimestamp > localTimestamp;
  362               } else {
  363                   return localTimestamp > remoteTimestamp;
  364               }
  365           } catch (IOException ex) {
  366               return false;
  367           }
  368       }
  369   
  370       /**
  371        * Sends a single file to the remote host. <code>filename</code> may
  372        * contain a relative path specification. When this is the case, <code>sendFile</code>
  373        * will attempt to create any necessary parent directories before sending
  374        * the file. The file will then be sent using the entire relative path
  375        * spec - no attempt is made to change directories. It is anticipated that
  376        * this may eventually cause problems with some FTP servers, but it
  377        * simplifies the coding.
  378        */
  379       protected void sendFile(SftpClient sftp, String dir, String filename)
  380           throws IOException, BuildException {
  381           InputStream instream = null;
  382           SftpFileOutputStream out = null;
  383   
  384           try {
  385               File file = parent.getProject().resolveFile(new File(dir, filename).getPath());
  386               String remotefile = resolveFile(filename);
  387   
  388               if (newerOnly && isUpToDate(sftp, file, remotefile)) {
  389                   return;
  390               }
  391   
  392               if (verbose) {
  393                   log("transferring " + file.getAbsolutePath() + " to " +
  394                       remotedir + remotefile);
  395               }
  396   
  397               instream = new BufferedInputStream(new FileInputStream(file));
  398               createParents(sftp, filename);
  399               sftp.put(file.getAbsolutePath(), remotefile);
  400   
  401               // Set the umask
  402               sftp.chmod(Integer.parseInt(chmod, 8), remotefile);
  403               log("File " + file.getAbsolutePath() + " copied to " + parent.host,
  404                   Project.MSG_VERBOSE);
  405               transferred++;
  406           } catch (IOException ex1) {
  407               String s = "Could not put file: " + ex1.getMessage();
  408   
  409               if (skipFailedTransfers == true) {
  410                   log(s, Project.MSG_WARN);
  411                   skipped++;
  412               } else {
  413                   throw new BuildException(s);
  414               }
  415           } finally {
  416               try {
  417                   if (instream != null) {
  418                       instream.close();
  419                   }
  420               } catch (IOException ex) {
  421                   // ignore it
  422               }
  423   
  424               try {
  425                   if (out != null) {
  426                       out.close();
  427                   }
  428               } catch (IOException ex) {
  429               }
  430           }
  431       }
  432   
  433       /** Delete a file from the remote host.  */
  434       protected void delFile(SftpClient sftp, String filename)
  435           throws IOException, BuildException {
  436           if (verbose) {
  437               log("deleting " + filename);
  438           }
  439   
  440           try {
  441               String remotefile = resolveFile(filename);
  442               sftp.rm(remotefile);
  443               log("File " + filename + " deleted from " + parent.host,
  444                   Project.MSG_VERBOSE);
  445               transferred++;
  446           } catch (IOException ex) {
  447               String s = "could not delete file: " + ex.getMessage();
  448   
  449               if (skipFailedTransfers == true) {
  450                   log(s, Project.MSG_WARN);
  451                   skipped++;
  452               } else {
  453                   throw new BuildException(s);
  454               }
  455           }
  456       }
  457   
  458       protected void chmod(SftpClient sftp, String filename)
  459           throws IOException, BuildException {
  460           sftp.chmod(Integer.parseInt(chmod, 8), resolveFile(filename));
  461       }
  462   
  463       /**
  464        * Retrieve a single file to the remote host. <code>filename</code> may
  465        * contain a relative path specification. <p>
  466        *
  467        * The file will then be retreived using the entire relative path spec -
  468        * no attempt is made to change directories. It is anticipated that this
  469        * may eventually cause problems with some FTP servers, but it simplifies
  470        * the coding.</p>
  471        */
  472       protected void getFile(SftpClient sftp, String dir, String filename)
  473           throws IOException, BuildException {
  474           try {
  475               String localfile = filename;
  476   
  477               if (localfile.indexOf("/") >= 0) {
  478                   localfile = localfile.substring(filename.lastIndexOf("/"));
  479               }
  480   
  481               File file = parent.getProject().resolveFile(new File(dir, localfile).getAbsolutePath());
  482               log(dir);
  483               log(filename);
  484               log(file.getAbsolutePath());
  485   
  486               if (newerOnly && isUpToDate(sftp, file, resolveFile(filename))) {
  487                   return;
  488               }
  489   
  490               if (verbose) {
  491                   log("transferring " + filename + " to " +
  492                       file.getAbsolutePath());
  493               }
  494   
  495               File pdir = fileUtils.getParentFile(file);
  496   
  497               if (!pdir.exists()) {
  498                   pdir.mkdirs();
  499               }
  500   
  501               //sftp.lcd(dir);
  502               // Get the file
  503               sftp.get(filename, file.getAbsolutePath());
  504   
  505               if (verbose) {
  506                   log("File " + file.getAbsolutePath() + " copied from " +
  507                       parent.host);
  508               }
  509   
  510               FileAttributes attrs = sftp.stat(filename);
  511               file.setLastModified(attrs.getModifiedTime().longValue() * 1000);
  512               transferred++;
  513           } catch (IOException ioe) {
  514               String s = "could not get file: " + ioe.getMessage();
  515   
  516               if (skipFailedTransfers == true) {
  517                   log(s, Project.MSG_WARN);
  518                   skipped++;
  519               } else {
  520                   throw new BuildException(s);
  521               }
  522           }
  523       }
  524   
  525       /**
  526        * Create the specified directory on the remote host.
  527        *
  528        * @param sftp The SFTP client connection
  529        * @param dir The directory to create
  530        */
  531       protected void makeRemoteDir(SftpClient sftp, String dir)
  532           throws BuildException {
  533           if (verbose) {
  534               log("creating directory: " + dir);
  535           }
  536   
  537           try {
  538               sftp.mkdir(dir);
  539           } catch (IOException ex) {
  540               log(ex.getMessage());
  541           }
  542       }
  543   
  544       /** Runs the task.  */
  545       public void execute(SshClient ssh) throws BuildException {
  546           try {
  547               Integer.parseInt(chmod, 8);
  548           } catch (NumberFormatException ex) {
  549               throw new BuildException(
  550                   "chmod attribute format is incorrect, use octal number format i.e 0777");
  551           }
  552   
  553           executeSFTPTask(ssh);
  554       }
  555   
  556       protected void executeSFTPTask(SshClient ssh) throws BuildException {
  557           SftpClient sftp = null;
  558   
  559           try {
  560               sftp = ssh.openSftpClient();
  561   
  562               if (action == MK_DIR) {
  563                   makeRemoteDir(sftp, remotedir);
  564               } else {
  565                   if (remotedir.trim().length() > 0) {
  566                       log("Setting the remote directory "); //, Project.MSG_VERBOSE);
  567                       sftp.cd(remotedir);
  568                   }
  569   
  570                   // Get the absolute path of the remote directory
  571                   remotedir = sftp.pwd();
  572                   log("Remote directory is " + remotedir);
  573   
  574                   if (!remotedir.endsWith("/")) {
  575                       remotedir += "/";
  576                   }
  577   
  578                   log(ACTION_STRS[action] + " files");
  579                   transferFiles(sftp);
  580               }
  581           } catch (IOException ex) {
  582               throw new BuildException("error during SFTP transfer: " + ex);
  583           } finally {
  584               if ((sftp != null) && !sftp.isClosed()) {
  585                   try {
  586                       log("Quiting SFTP", Project.MSG_VERBOSE);
  587                       sftp.quit();
  588                   } catch (IOException ex) {
  589                       // ignore it
  590                   }
  591               }
  592           }
  593       }
  594   
  595       protected class SftpDirectoryScanner extends DirectoryScanner {
  596           protected SftpClient sftp = null;
  597   
  598           public SftpDirectoryScanner(SftpClient sftp) {
  599               super();
  600               this.sftp = sftp;
  601           }
  602   
  603           public void scan() {
  604               if (includes == null) {
  605                   // No includes supplied, so set it to 'matches all'
  606                   includes = new String[1];
  607                   includes[0] = "**";
  608               }
  609   
  610               if (excludes == null) {
  611                   excludes = new String[0];
  612               }
  613   
  614               filesIncluded = new Vector();
  615               filesNotIncluded = new Vector();
  616               filesExcluded = new Vector();
  617               dirsIncluded = new Vector();
  618               dirsNotIncluded = new Vector();
  619               dirsExcluded = new Vector();
  620               scandir(remotedir, true);
  621           }
  622   
  623           protected void scandir(String dir, boolean fast) {
  624               try {
  625                   List children = sftp.ls(dir);
  626   
  627                   if (!dir.endsWith("/")) {
  628                       dir += "/";
  629                   }
  630   
  631                   Iterator it = children.iterator();
  632   
  633                   while (it.hasNext()) {
  634                       SftpFile file = (SftpFile) it.next();
  635   
  636                       if (!file.getFilename().equals(".") &&
  637                               !file.getFilename().equals("..")) {
  638                           if (file.isDirectory()) {
  639                               String name = dir + file.getFilename();
  640   
  641                               if (isIncluded(name)) {
  642                                   if (!isExcluded(name)) {
  643                                       dirsIncluded.addElement(name);
  644   
  645                                       if (fast) {
  646                                           scandir(dir + file.getFilename(), fast);
  647                                       }
  648                                   } else {
  649                                       dirsExcluded.addElement(name);
  650   
  651                                       if (fast && couldHoldIncluded(name)) {
  652                                           scandir(dir + file.getFilename(), fast);
  653                                       }
  654                                   }
  655                               } else {
  656                                   dirsNotIncluded.addElement(name);
  657   
  658                                   if (fast && couldHoldIncluded(name)) {
  659                                       scandir(dir + file.getFilename(), fast);
  660                                   }
  661                               }
  662   
  663                               if (!fast) {
  664                                   scandir(dir + file.getFilename(), fast);
  665                               }
  666                           } else {
  667                               if (file.isFile()) {
  668                                   String name = dir + file.getFilename();
  669   
  670                                   if (isIncluded(name)) {
  671                                       if (!isExcluded(name)) {
  672                                           filesIncluded.addElement(name);
  673                                       } else {
  674                                           filesExcluded.addElement(name);
  675                                       }
  676                                   } else {
  677                                       filesNotIncluded.addElement(name);
  678                                   }
  679                               }
  680                           }
  681                       }
  682                   }
  683   
  684                   //ftp.changeToParentDirectory();
  685               } catch (IOException e) {
  686                   throw new BuildException("Error while communicating with SFTP ",
  687                       e);
  688               }
  689           }
  690       }
  691   
  692       /**
  693        * an action to perform, one of
  694        * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod"
  695        */
  696       public static class Action extends EnumeratedAttribute {
  697           private static final String[] validActions = {
  698               "send", "put", "recv", "get", "del", "delete", "list", "mkdir",
  699               "chmod"
  700           };
  701   
  702           public String[] getValues() {
  703               return validActions;
  704           }
  705   
  706           public int getAction() {
  707               String actionL = getValue().toLowerCase(Locale.US);
  708   
  709               if (actionL.equals("send") || actionL.equals("put")) {
  710                   return SEND_FILES;
  711               } else if (actionL.equals("recv") || actionL.equals("get")) {
  712                   return GET_FILES;
  713               } else if (actionL.equals("del") || actionL.equals("delete")) {
  714                   return DEL_FILES;
  715               }
  716               /*else if (actionL.equals("list")) {
  717                 return LIST_FILES;
  718                      }*/
  719               else if (actionL.equals("chmod")) {
  720                   return CHMOD;
  721               } else if (actionL.equals("mkdir")) {
  722                   return MK_DIR;
  723               }
  724   
  725               return SEND_FILES;
  726           }
  727       }
  728   }

Save This Page
Home » j2ssh-0.2.9-src » com.sshtools.ant » [javadoc | source]