Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
Apache Ant Source Code Files
Apache Ant Source Code Files are inside the Apache Ant source package file like apache-ant-1.10.10-src.zip. Unzip the source package file and go to the "src/main" sub-directory, you will see source code files.
Here is the list of Java source code files of the Apache Ant 1.10.10 in \Users\fyicenter\apache-ant-1.10.10\src\main:
✍: FYIcenter.com
⏎ org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.taskdefs.optional.net; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.Vector; import java.util.function.Predicate; import java.util.stream.Stream; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPReply; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Delete; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.selectors.SelectorUtils; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.RetryHandler; import org.apache.tools.ant.util.Retryable; import org.apache.tools.ant.util.VectorSet; public class FTPTaskMirrorImpl implements FTPTaskMirror { /** return code of ftp */ private static final int CODE_521 = 521; private static final int CODE_550 = 550; private static final int CODE_553 = 553; /** Date formatter used in logging, note not thread safe! */ private static final SimpleDateFormat TIMESTAMP_LOGGING_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); private final FTPTask task; private Set<File> dirCache = new HashSet<>(); private int transferred = 0; private int skipped = 0; /** * Constructor. * @param task the FTPTask that uses this mirror. */ public FTPTaskMirrorImpl(FTPTask task) { this.task = task; } /** * internal class providing a File-like interface to some of the information * available from the FTP server * */ protected static class FTPFileProxy extends File { private static final long serialVersionUID = 1L; private final FTPFile file; private final String[] parts; private final String name; /** * creates a proxy to a FTP file * @param file FTPFile */ public FTPFileProxy(FTPFile file) { super(file.getName()); name = file.getName(); this.file = file; parts = FileUtils.getPathStack(name); } /** * creates a proxy to a FTP directory * @param completePath the remote directory. */ public FTPFileProxy(String completePath) { super(completePath); file = null; name = completePath; parts = FileUtils.getPathStack(completePath); } /* (non-Javadoc) * @see java.io.File#exists() */ @Override public boolean exists() { return true; } /* (non-Javadoc) * @see java.io.File#getAbsolutePath() */ @Override public String getAbsolutePath() { return name; } /* (non-Javadoc) * @see java.io.File#getName() */ @Override public String getName() { return parts.length > 0 ? parts[parts.length - 1] : name; } /* (non-Javadoc) * @see java.io.File#getParent() */ @Override public String getParent() { StringBuilder result = new StringBuilder(); for (int i = 0; i < parts.length - 1; i++) { result.append(File.separatorChar).append(parts[i]); } return result.toString(); } /* (non-Javadoc) * @see java.io.File#getPath() */ @Override public String getPath() { return name; } /** * FTP files are stored as absolute paths * @return true */ @Override public boolean isAbsolute() { return true; } /* (non-Javadoc) * @see java.io.File#isDirectory() */ @Override public boolean isDirectory() { return file == null; } /* (non-Javadoc) * @see java.io.File#isFile() */ @Override public boolean isFile() { return file != null; } /** * FTP files cannot be hidden * * @return false */ @Override public boolean isHidden() { return false; } /* (non-Javadoc) * @see java.io.File#lastModified() */ @Override public long lastModified() { if (file != null) { return file.getTimestamp().getTimeInMillis(); } return 0; } /* (non-Javadoc) * @see java.io.File#length() */ @Override public long length() { if (file != null) { return file.getSize(); } return 0; } } /** * internal class allowing to read the contents of a remote file system * using the FTP protocol * used in particular for ftp get operations * differences with DirectoryScanner * "" (the root of the fileset) is never included in the included directories * followSymlinks defaults to false */ protected class FTPDirectoryScanner extends DirectoryScanner { // CheckStyle:VisibilityModifier OFF - bc protected FTPClient ftp = null; // CheckStyle:VisibilityModifier ON private String rootPath = null; /** * since ant 1.6 * this flag should be set to true on UNIX and can save scanning time */ private boolean remoteSystemCaseSensitive = false; private boolean remoteSensitivityChecked = false; /** * constructor * @param ftp ftpclient object */ public FTPDirectoryScanner(FTPClient ftp) { super(); this.ftp = ftp; this.setFollowSymlinks(false); } /** * scans the remote directory, * storing internally the included files, directories, ... */ @Override public void scan() { if (includes == null) { // No includes supplied, so set it to 'matches all' includes = new String[1]; includes[0] = "**"; } if (excludes == null) { excludes = new String[0]; } filesIncluded = new VectorSet<>(); filesNotIncluded = new Vector<>(); filesExcluded = new VectorSet<>(); dirsIncluded = new VectorSet<>(); dirsNotIncluded = new Vector<>(); dirsExcluded = new VectorSet<>(); try { String cwd = ftp.printWorkingDirectory(); // always start from the current ftp working dir forceRemoteSensitivityCheck(); checkIncludePatterns(); clearCaches(); ftp.changeWorkingDirectory(cwd); } catch (IOException e) { throw new BuildException("Unable to scan FTP server: ", e); } } /** * this routine is actually checking all the include patterns in * order to avoid scanning everything under base dir * @since ant1.6 */ private void checkIncludePatterns() { Map<String, String> newroots = new Hashtable<>(); // put in the newroots vector the include patterns without // wildcard tokens for (String include : includes) { String newpattern = SelectorUtils.rtrimWildcardTokens(include); newroots.put(newpattern, include); } if (task.getRemotedir() == null) { try { task.setRemotedir(ftp.printWorkingDirectory()); } catch (IOException e) { throw new BuildException("could not read current ftp directory", task.getLocation()); } } AntFTPFile baseFTPFile = new AntFTPRootFile(ftp, task.getRemotedir()); rootPath = baseFTPFile.getAbsolutePath(); // construct it if (newroots.containsKey("")) { // we are going to scan everything anyway scandir(rootPath, "", true); } else { // only scan directories that can include matched files or // directories newroots.forEach( (k, v) -> scanRoot(new AntFTPFile(baseFTPFile, k), v)); } } private void scanRoot(AntFTPFile myfile, String originalpattern) { String currentelement; boolean isOK = true; boolean traversesSymlinks = false; String path = null; if (myfile.exists()) { forceRemoteSensitivityCheck(); if (remoteSensitivityChecked && remoteSystemCaseSensitive && isFollowSymlinks()) { // cool case, //we do not need to scan all the subdirs in the relative path path = myfile.getFastRelativePath(); } else { // may be on a case insensitive file system. We want // the results to show what's really on the disk, so // we need to double check. try { path = myfile.getRelativePath(); traversesSymlinks = myfile.isTraverseSymlinks(); } catch (IOException be) { throw new BuildException(be, task.getLocation()); } catch (BuildException be) { isOK = false; } } } else { isOK = false; } if (isOK) { currentelement = path.replace(task.getSeparator().charAt(0), File.separatorChar); if (!isFollowSymlinks() && traversesSymlinks) { return; } if (myfile.isDirectory()) { if (isIncluded(currentelement) && !currentelement.isEmpty()) { accountForIncludedDir(currentelement, myfile, true); } else { if (!currentelement.isEmpty() && currentelement.charAt(currentelement.length() - 1) != File.separatorChar) { currentelement += File.separatorChar; } scandir(myfile.getAbsolutePath(), currentelement, true); } } else if (isCaseSensitive && originalpattern.equals(currentelement)) { accountForIncludedFile(currentelement); } else if (!isCaseSensitive && originalpattern .equalsIgnoreCase(currentelement)) { accountForIncludedFile(currentelement); } } } /** * scans a particular directory. populates the scannedDirs cache. * * @param dir directory to scan * @param vpath relative path to the base directory of the remote fileset * always ended with a File.separator * @param fast seems to be always true in practice */ protected void scandir(String dir, String vpath, boolean fast) { // avoid double scanning of directories, can only happen in fast mode if (fast && hasBeenScanned(vpath)) { return; } try { if (!ftp.changeWorkingDirectory(dir)) { return; } String completePath; if (vpath.isEmpty()) { completePath = rootPath; } else { completePath = rootPath + task.getSeparator() + vpath.replace(File.separatorChar, task.getSeparator().charAt(0)); } FTPFile[] newfiles = listFiles(completePath, false); if (newfiles == null) { ftp.changeToParentDirectory(); return; } for (FTPFile file : newfiles) { if (file != null && !".".equals(file.getName()) && !"..".equals(file.getName())) { String name = vpath + file.getName(); scannedDirs.put(name, new FTPFileProxy(file)); if (isFunctioningAsDirectory(ftp, dir, file)) { boolean slowScanAllowed = true; if (!isFollowSymlinks() && file.isSymbolicLink()) { dirsExcluded.addElement(name); slowScanAllowed = false; } else if (isIncluded(name)) { accountForIncludedDir(name, new AntFTPFile(ftp, file, completePath), fast); } else { dirsNotIncluded.addElement(name); if (fast && couldHoldIncluded(name)) { scandir(file.getName(), name + File.separator, fast); } } if (!fast && slowScanAllowed) { scandir(file.getName(), name + File.separator, fast); } } else { if (!isFollowSymlinks() && file.isSymbolicLink()) { filesExcluded.addElement(name); } else if (isFunctioningAsFile(ftp, dir, file)) { accountForIncludedFile(name); } } } } ftp.changeToParentDirectory(); } catch (IOException e) { throw new BuildException("Error while communicating with FTP " + "server: ", e); } } /** * process included file * @param name path of the file relative to the directory of the fileset */ private void accountForIncludedFile(String name) { if (!filesIncluded.contains(name) && !filesExcluded.contains(name)) { if (isIncluded(name)) { if (!isExcluded(name) && isSelected(name, scannedDirs.get(name))) { filesIncluded.addElement(name); } else { filesExcluded.addElement(name); } } else { filesNotIncluded.addElement(name); } } } /** * * @param name path of the directory relative to the directory of * the fileset * @param file directory as file * @param fast boolean */ private void accountForIncludedDir(String name, AntFTPFile file, boolean fast) { if (!dirsIncluded.contains(name) && !dirsExcluded.contains(name)) { if (!isExcluded(name)) { if (fast) { if (file.isSymbolicLink()) { try { file.getClient().changeWorkingDirectory(file.curpwd); } catch (IOException ioe) { throw new BuildException("could not change directory to curpwd"); } scandir(file.getLink(), name + File.separator, fast); } else { try { file.getClient().changeWorkingDirectory(file.curpwd); } catch (IOException ioe) { throw new BuildException("could not change directory to curpwd"); } scandir(file.getName(), name + File.separator, fast); } } dirsIncluded.addElement(name); } else { dirsExcluded.addElement(name); if (fast && couldHoldIncluded(name)) { try { file.getClient().changeWorkingDirectory(file.curpwd); } catch (IOException ioe) { throw new BuildException("could not change directory to curpwd"); } scandir(file.getName(), name + File.separator, fast); } } } } /** * temporary table to speed up the various scanning methods below * * @since Ant 1.6 */ private Map<String, FTPFile[]> fileListMap = new HashMap<>(); /** * List of all scanned directories. * * @since Ant 1.6 */ private Map<String, FTPFileProxy> scannedDirs = new HashMap<>(); /** * Has the directory with the given path relative to the base * directory already been scanned? * * @since Ant 1.6 */ private boolean hasBeenScanned(String vpath) { return scannedDirs.containsKey(vpath); } /** * Clear internal caches. * * @since Ant 1.6 */ private void clearCaches() { fileListMap.clear(); scannedDirs.clear(); } /** * list the files present in one directory. * @param directory full path on the remote side * @param changedir if true change to directory directory before listing * @return array of FTPFile */ public FTPFile[] listFiles(String directory, boolean changedir) { //task.log("listing files in directory " + directory, Project.MSG_DEBUG); String currentPath = directory; if (changedir) { try { if (!ftp.changeWorkingDirectory(directory)) { return null; } currentPath = ftp.printWorkingDirectory(); } catch (IOException ioe) { throw new BuildException(ioe, task.getLocation()); } } if (fileListMap.containsKey(currentPath)) { task.log("filelist map used in listing files", Project.MSG_DEBUG); return fileListMap.get(currentPath); } FTPFile[] result; try { result = ftp.listFiles(); } catch (IOException ioe) { throw new BuildException(ioe, task.getLocation()); } fileListMap.put(currentPath, result); if (!remoteSensitivityChecked) { checkRemoteSensitivity(result, directory); } return result; } private void forceRemoteSensitivityCheck() { if (!remoteSensitivityChecked) { try { checkRemoteSensitivity(ftp.listFiles(), ftp.printWorkingDirectory()); } catch (IOException ioe) { throw new BuildException(ioe, task.getLocation()); } } } /** * cd into one directory and * list the files present in one directory. * @param directory full path on the remote side * @return array of FTPFile */ public FTPFile[] listFiles(String directory) { return listFiles(directory, true); } private void checkRemoteSensitivity(FTPFile[] array, String directory) { if (array == null) { return; } boolean candidateFound = false; String target = null; for (int icounter = 0; icounter < array.length; icounter++) { if (array[icounter] != null && array[icounter].isDirectory()) { if (!".".equals(array[icounter].getName()) && !"..".equals(array[icounter].getName())) { candidateFound = true; target = fiddleName(array[icounter].getName()); task.log("will try to cd to " + target + " where a directory called " + array[icounter].getName() + " exists", Project.MSG_DEBUG); for (int pcounter = 0; pcounter < array.length; pcounter++) { if (array[pcounter] != null && pcounter != icounter && target.equals(array[pcounter].getName())) { candidateFound = false; break; } } if (candidateFound) { break; } } } } if (candidateFound) { try { task.log("testing case sensitivity, attempting to cd to " + target, Project.MSG_DEBUG); remoteSystemCaseSensitive = !ftp.changeWorkingDirectory(target); } catch (IOException ioe) { remoteSystemCaseSensitive = true; } finally { try { ftp.changeWorkingDirectory(directory); } catch (IOException ioe) { throw new BuildException(ioe, task.getLocation()); //NOSONAR } } task.log("remote system is case sensitive : " + remoteSystemCaseSensitive, Project.MSG_VERBOSE); remoteSensitivityChecked = true; } } private String fiddleName(String origin) { StringBuilder result = new StringBuilder(); for (char ch : origin.toCharArray()) { if (Character.isLowerCase(ch)) { result.append(Character.toUpperCase(ch)); } else if (Character.isUpperCase(ch)) { result.append(Character.toLowerCase(ch)); } else { result.append(ch); } } return result.toString(); } /** * an AntFTPFile is a representation of a remote file * @since Ant 1.6 */ protected class AntFTPFile { /** * ftp client */ private FTPClient client; /** * parent directory of the file */ private String curpwd; /** * the file itself */ private FTPFile ftpFile; /** * */ private AntFTPFile parent = null; private boolean relativePathCalculated = false; private boolean traversesSymlinks = false; private String relativePath = ""; /** * constructor * @param client ftp client variable * @param ftpFile the file * @param curpwd absolute remote path where the file is found */ public AntFTPFile(FTPClient client, FTPFile ftpFile, String curpwd) { this.client = client; this.ftpFile = ftpFile; this.curpwd = curpwd; } /** * other constructor * @param parent the parent file * @param path a relative path to the parent file */ public AntFTPFile(AntFTPFile parent, String path) { this.parent = parent; this.client = parent.client; List<String> pathElements = SelectorUtils.tokenizePath(path); try { boolean result = this.client.changeWorkingDirectory(parent.getAbsolutePath()); //this should not happen, except if parent has been deleted by another process if (!result) { return; } this.curpwd = parent.getAbsolutePath(); } catch (IOException ioe) { throw new BuildException("could not change working dir to %s", parent.curpwd); } for (String currentPathElement : pathElements) { try { if (!this.client.changeWorkingDirectory(currentPathElement)) { if (!isCaseSensitive() && (remoteSystemCaseSensitive || !remoteSensitivityChecked)) { currentPathElement = findPathElementCaseUnsensitive(this.curpwd, currentPathElement); if (currentPathElement == null) { return; } } return; } this.curpwd = getCurpwdPlusFileSep() + currentPathElement; } catch (IOException ioe) { throw new BuildException("could not change working dir to %s from %s", currentPathElement, this.curpwd); } } String lastpathelement = pathElements.get(pathElements.size() - 1); this.ftpFile = getFile(listFiles(this.curpwd), lastpathelement); } /** * find a file in a directory in case insensitive way * @param parentPath where we are * @param soughtPathElement what is being sought * @return the first file found or null if not found */ private String findPathElementCaseUnsensitive(String parentPath, String soughtPathElement) { // we are already in the right path, so the second parameter // is false FTPFile[] files = listFiles(parentPath, false); if (files == null) { return null; } for (FTPFile file : files) { if (file != null && file.getName().equalsIgnoreCase(soughtPathElement)) { return file.getName(); } } return null; } /** * find out if the file exists * @return true if the file exists */ public boolean exists() { return ftpFile != null; } /** * if the file is a symbolic link, find out to what it is pointing * @return the target of the symbolic link */ public String getLink() { return ftpFile.getLink(); } /** * get the name of the file * @return the name of the file */ public String getName() { return ftpFile.getName(); } /** * find out the absolute path of the file * @return absolute path as string */ public String getAbsolutePath() { return getCurpwdPlusFileSep() + ftpFile.getName(); } /** * find out the relative path assuming that the path used to construct * this AntFTPFile was spelled properly with regards to case. * This is OK on a case sensitive system such as UNIX * @return relative path */ public String getFastRelativePath() { String absPath = getAbsolutePath(); if (absPath.startsWith(rootPath + task.getSeparator())) { return absPath.substring(rootPath.length() + task.getSeparator().length()); } return null; } /** * find out the relative path to the rootPath of the enclosing scanner. * this relative path is spelled exactly like on disk, * for instance if the AntFTPFile has been instantiated as ALPHA, * but the file is really called alpha, this method will return alpha. * If a symbolic link is encountered, it is followed, but the name of the link * rather than the name of the target is returned. * (ie does not behave like File.getCanonicalPath()) * @return relative path, separated by remoteFileSep * @throws IOException if a change directory fails, ... * @throws BuildException if one of the components of the relative path cannot * be found. */ public String getRelativePath() throws IOException, BuildException { if (!relativePathCalculated) { if (parent != null) { traversesSymlinks = parent.isTraverseSymlinks(); relativePath = getRelativePath(parent.getAbsolutePath(), parent.getRelativePath()); } else { relativePath = getRelativePath(rootPath, ""); relativePathCalculated = true; } } return relativePath; } /** * get the relative path of this file * @param currentPath base path * @param currentRelativePath relative path of the base path with regards to remote dir * @return relative path */ private String getRelativePath(String currentPath, String currentRelativePath) { List<String> pathElements = SelectorUtils .tokenizePath(getAbsolutePath(), task.getSeparator()); List<String> pathElements2 = SelectorUtils .tokenizePath(currentPath, task.getSeparator()); StringBuilder relPath = new StringBuilder(currentRelativePath); final int size = pathElements.size(); for (int pcount = pathElements2.size(); pcount < size; pcount++) { String currentElement = pathElements.get(pcount); FTPFile[] theFiles = listFiles(currentPath); FTPFile theFile = null; if (theFiles != null) { theFile = getFile(theFiles, currentElement); } if (relPath.length() > 0) { relPath.append(task.getSeparator()); } if (theFile == null) { // hit a hidden file assume not a symlink relPath.append(currentElement); currentPath += task.getSeparator() + currentElement; task.log("Hidden file " + relPath + " assumed to not be a symlink.", Project.MSG_VERBOSE); } else { traversesSymlinks = traversesSymlinks || theFile.isSymbolicLink(); relPath.append(theFile.getName()); currentPath += task.getSeparator() + theFile.getName(); } } return relPath.toString(); } /** * find a file matching a string in an array of FTPFile. * This method will find "alpha" when requested for "ALPHA" * if and only if the caseSensitive attribute is set to false. * When caseSensitive is set to true, only the exact match is returned. * @param theFiles array of files * @param lastpathelement the file name being sought * @return null if the file cannot be found, otherwise return the matching file. */ public FTPFile getFile(FTPFile[] theFiles, String lastpathelement) { if (theFiles == null) { return null; } Predicate<String> test = isCaseSensitive() ? lastpathelement::equals : lastpathelement::equalsIgnoreCase; return Stream.of(theFiles) .filter(Objects::nonNull) .filter(f -> test.test(f.getName())) .findFirst().orElse(null); } /** * tell if a file is a directory. * note that it will return false for symbolic links pointing to directories. * @return <code>true</code> for directories */ public boolean isDirectory() { return ftpFile.isDirectory(); } /** * tell if a file is a symbolic link * @return <code>true</code> for symbolic links */ public boolean isSymbolicLink() { return ftpFile.isSymbolicLink(); } /** * return the attached FTP client object. * Warning : this instance is really shared with the enclosing class. * @return FTP client */ protected FTPClient getClient() { return client; } /** * sets the current path of an AntFTPFile * @param curpwd the current path one wants to set */ protected void setCurpwd(String curpwd) { this.curpwd = curpwd; } /** * returns the path of the directory containing the AntFTPFile. * of the full path of the file itself in case of AntFTPRootFile * @return parent directory of the AntFTPFile */ public String getCurpwd() { return curpwd; } /** * returns the path of the directory containing the AntFTPFile. * of the full path of the file itself in case of AntFTPRootFile * and appends the remote file separator if necessary. * @return parent directory of the AntFTPFile * @since Ant 1.8.2 */ public String getCurpwdPlusFileSep() { String sep = task.getSeparator(); return curpwd.endsWith(sep) ? curpwd : curpwd + sep; } /** * find out if a symbolic link is encountered in the relative path of this file * from rootPath. * @return <code>true</code> if a symbolic link is encountered in the relative path. * @throws IOException if one of the change directory or directory listing operations * fails * @throws BuildException if a path component in the relative path cannot be found. */ public boolean isTraverseSymlinks() throws IOException, BuildException { if (!relativePathCalculated) { // getRelativePath also finds about symlinks getRelativePath(); } return traversesSymlinks; } /** * Get a string rep of this object. * @return a string containing the pwd and the file. */ @Override public String toString() { return "AntFtpFile: " + curpwd + "%" + ftpFile; } } /** * special class to represent the remote directory itself * @since Ant 1.6 */ protected class AntFTPRootFile extends AntFTPFile { private String remotedir; /** * constructor * @param aclient FTP client * @param remotedir remote directory */ public AntFTPRootFile(FTPClient aclient, String remotedir) { super(aclient, null, remotedir); this.remotedir = remotedir; try { this.getClient().changeWorkingDirectory(this.remotedir); this.setCurpwd(this.getClient().printWorkingDirectory()); } catch (IOException ioe) { throw new BuildException(ioe, task.getLocation()); } } /** * find the absolute path * @return absolute path */ @Override public String getAbsolutePath() { return this.getCurpwd(); } /** * find out the relative path to root * @return empty string * @throws BuildException actually never * @throws IOException actually never */ @Override public String getRelativePath() throws BuildException, IOException { return ""; } } } /** * check FTPFiles to check whether they function as directories too * the FTPFile API seem to make directory and symbolic links incompatible * we want to find out if we can cd to a symbolic link * @param dir the parent directory of the file to test * @param file the file to test * @return true if it is possible to cd to this directory * @since ant 1.6 */ private boolean isFunctioningAsDirectory(FTPClient ftp, String dir, FTPFile file) { boolean result = false; String currentWorkingDir = null; if (file.isDirectory()) { return true; } if (file.isFile()) { return false; } try { currentWorkingDir = ftp.printWorkingDirectory(); } catch (IOException ioe) { task.log("could not find current working directory " + dir + " while checking a symlink", Project.MSG_DEBUG); } if (currentWorkingDir != null) { try { result = ftp.changeWorkingDirectory(file.getLink()); } catch (IOException ioe) { task.log("could not cd to " + file.getLink() + " while checking a symlink", Project.MSG_DEBUG); } if (result) { boolean comeback = false; try { comeback = ftp.changeWorkingDirectory(currentWorkingDir); } catch (IOException ioe) { task.log("could not cd back to " + dir + " while checking a symlink", Project.MSG_ERR); } finally { if (!comeback) { throw new BuildException( "could not cd back to %s while checking a symlink", dir); } } } } return result; } /** * check FTPFiles to check whether they function as directories too * the FTPFile API seem to make directory and symbolic links incompatible * we want to find out if we can cd to a symbolic link * @param dir the parent directory of the file to test * @param file the file to test * @return true if it is possible to cd to this directory * @since ant 1.6 */ private boolean isFunctioningAsFile(FTPClient ftp, String dir, FTPFile file) { return !file.isDirectory() && (file.isFile() || !isFunctioningAsDirectory(ftp, dir, file)); } /** * Executable a retryable object. * @param h the retry handler. * @param r the object that should be retried until it succeeds * or the number of retries is reached. * @param descr a description of the command that is being run. * @throws IOException if there is a problem. */ protected void executeRetryable(RetryHandler h, Retryable r, String descr) throws IOException { h.execute(r, descr); } /** * For each file in the fileset, do the appropriate action: send, get, * delete, or list. * * @param ftp the FTPClient instance used to perform FTP actions * @param fs the fileset on which the actions are performed. * * @return the number of files to be transferred. * * @throws IOException if there is a problem reading a file * @throws BuildException if there is a problem in the configuration. */ protected int transferFiles(final FTPClient ftp, FileSet fs) throws IOException, BuildException { DirectoryScanner ds; if (task.getAction() == FTPTask.SEND_FILES) { ds = fs.getDirectoryScanner(task.getProject()); } else { ds = new FTPDirectoryScanner(ftp); fs.setupDirectoryScanner(ds, task.getProject()); ds.setFollowSymlinks(fs.isFollowSymlinks()); ds.scan(); } String[] dsfiles = null; if (task.getAction() == FTPTask.RM_DIR) { dsfiles = ds.getIncludedDirectories(); } else { dsfiles = ds.getIncludedFiles(); } if (ds.getBasedir() == null && (task.getAction() == FTPTask.SEND_FILES || task.getAction() == FTPTask.GET_FILES)) { throw new BuildException( "the dir attribute must be set for send and get actions"); } String dir = null; if (task.getAction() == FTPTask.SEND_FILES || task.getAction() == FTPTask.GET_FILES) { dir = ds.getBasedir().getAbsolutePath(); } // If we are doing a listing, we need the output stream created now. BufferedWriter bw = null; try { if (task.getAction() == FTPTask.LIST_FILES) { File pd = task.getListing().getParentFile(); if (!pd.exists()) { pd.mkdirs(); } bw = new BufferedWriter(new FileWriter(task.getListing())); } RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task); if (task.getAction() == FTPTask.RM_DIR) { // to remove directories, start by the end of the list // the trunk does not let itself be removed before the leaves for (int i = dsfiles.length - 1; i >= 0; i--) { final String dsfile = dsfiles[i]; executeRetryable(h, () -> rmDir(ftp, dsfile), dsfile); } } else { final BufferedWriter fbw = bw; final String fdir = dir; if (task.isNewer()) { task.setGranularityMillis(task.getTimestampGranularity() .getMilliseconds(task.getAction())); } for (final String dsfile : dsfiles) { executeRetryable(h, () -> { switch (task.getAction()) { case FTPTask.SEND_FILES: sendFile(ftp, fdir, dsfile); break; case FTPTask.GET_FILES: getFile(ftp, fdir, dsfile); break; case FTPTask.DEL_FILES: delFile(ftp, dsfile); break; case FTPTask.LIST_FILES: listFile(ftp, fbw, dsfile); break; case FTPTask.CHMOD: doSiteCommand(ftp, "chmod " + task.getChmod() + " " + resolveFile(dsfile)); transferred++; break; default: throw new BuildException("unknown ftp action %s", task.getAction()); } }, dsfile); } } } finally { if (bw != null) { bw.close(); } } return dsfiles.length; } /** * Sends all files specified by the configured filesets to the remote * server. * * @param ftp the FTPClient instance used to perform FTP actions * * @throws IOException if there is a problem reading a file * @throws BuildException if there is a problem in the configuration. */ protected void transferFiles(FTPClient ftp) throws IOException, BuildException { transferred = 0; skipped = 0; if (task.getFilesets().isEmpty()) { throw new BuildException("at least one fileset must be specified."); } // get files from filesets for (FileSet fs : task.getFilesets()) { if (fs != null) { transferFiles(ftp, fs); } } task.log( transferred + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()] + " " + FTPTask.COMPLETED_ACTION_STRS[task.getAction()]); if (skipped != 0) { task.log( skipped + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()] + " were not successfully " + FTPTask.COMPLETED_ACTION_STRS[task.getAction()]); } } /** * Correct a file path to correspond to the remote host requirements. This * implementation currently assumes that the remote end can handle * Unix-style paths with forward-slash separators. This can be overridden * with the <code>separator</code> task parameter. No attempt is made to * determine what syntax is appropriate for the remote host. * * @param file the remote file name to be resolved * * @return the filename as it will appear on the server. */ protected String resolveFile(String file) { return file.replace(File.separator.charAt(0), task.getSeparator().charAt(0)); } /** * Creates all parent directories specified in a complete relative * pathname. Attempts to create existing directories will not cause * errors. * * @param ftp the FTP client instance to use to execute FTP actions on * the remote server. * @param filename the name of the file whose parents should be created. * @throws IOException under non documented circumstances * @throws BuildException if it is impossible to cd to a remote directory * */ protected void createParents(FTPClient ftp, String filename) throws IOException, BuildException { File dir = new File(filename); if (dirCache.contains(dir)) { return; } Vector<File> parents = new Vector<>(); String dirname; while ((dirname = dir.getParent()) != null) { File checkDir = new File(dirname); if (dirCache.contains(checkDir)) { break; } dir = checkDir; parents.addElement(dir); } // find first non cached dir int i = parents.size() - 1; if (i >= 0) { String cwd = ftp.printWorkingDirectory(); String parent = dir.getParent(); if (parent != null) { if (!ftp.changeWorkingDirectory(resolveFile(parent))) { throw new BuildException( "could not change to directory: %s", ftp.getReplyString()); } } while (i >= 0) { dir = parents.elementAt(i--); // check if dir exists by trying to change into it. if (!ftp.changeWorkingDirectory(dir.getName())) { // could not change to it - try to create it task.log("creating remote directory " + resolveFile(dir.getPath()), Project.MSG_VERBOSE); if (!ftp.makeDirectory(dir.getName())) { handleMkDirFailure(ftp); } if (!ftp.changeWorkingDirectory(dir.getName())) { throw new BuildException( "could not change to directory: %s", ftp.getReplyString()); } } dirCache.add(dir); } ftp.changeWorkingDirectory(cwd); } } /** * auto find the time difference between local and remote * @param ftp handle to ftp client * @return number of millis to add to remote time to make it comparable to local time * @since ant 1.6 */ private long getTimeDiff(FTPClient ftp) { long returnValue = 0; File tempFile = findFileName(ftp); try { // create a local temporary file FILE_UTILS.createNewFile(tempFile); long localTimeStamp = tempFile.lastModified(); BufferedInputStream instream = new BufferedInputStream(Files.newInputStream(tempFile.toPath())); ftp.storeFile(tempFile.getName(), instream); instream.close(); if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { FTPFile[] ftpFiles = ftp.listFiles(tempFile.getName()); if (ftpFiles.length == 1) { long remoteTimeStamp = ftpFiles[0].getTimestamp().getTime().getTime(); returnValue = localTimeStamp - remoteTimeStamp; } ftp.deleteFile(ftpFiles[0].getName()); } // delegate the deletion of the local temp file to the delete task // because of race conditions occurring on Windows Delete mydelete = new Delete(); mydelete.bindToOwner(task); mydelete.setFile(tempFile.getCanonicalFile()); mydelete.execute(); } catch (Exception e) { throw new BuildException(e, task.getLocation()); } return returnValue; } /** * find a suitable name for local and remote temporary file */ private File findFileName(FTPClient ftp) { FTPFile[] files = null; final int maxIterations = 1000; for (int counter = 1; counter < maxIterations; counter++) { File localFile = FILE_UTILS.createTempFile(task.getProject(), "ant" + Integer.toString(counter), ".tmp", null, false, false); String fileName = localFile.getName(); boolean found = false; try { if (files == null) { files = ftp.listFiles(); } for (FTPFile file : files) { if (file != null && file.getName().equals(fileName)) { found = true; break; } } } catch (IOException ioe) { throw new BuildException(ioe, task.getLocation()); } if (!found) { localFile.deleteOnExit(); return localFile; } } return null; } /** * Checks to see if the remote file is current as compared with the local * file. Returns true if the target file is up to date. * @param ftp ftpclient * @param localFile local file * @param remoteFile remote file * @return true if the target file is up to date * @throws IOException in unknown circumstances * @throws BuildException if the date of the remote files cannot be found and the action is * GET_FILES */ protected boolean isUpToDate(FTPClient ftp, File localFile, String remoteFile) throws IOException, BuildException { task.log("checking date for " + remoteFile, Project.MSG_VERBOSE); FTPFile[] files = ftp.listFiles(remoteFile); // For Microsoft's Ftp-Service an Array with length 0 is // returned if configured to return listings in "MS-DOS"-Format if (files == null || files.length == 0) { // If we are sending files, then assume out of date. // If we are getting files, then throw an error if (task.getAction() == FTPTask.SEND_FILES) { task.log("Could not date test remote file: " + remoteFile + "assuming out of date.", Project.MSG_VERBOSE); return false; } throw new BuildException("could not date test remote file: %s", ftp.getReplyString()); } long remoteTimestamp = files[0].getTimestamp().getTime().getTime(); long localTimestamp = localFile.lastModified(); long adjustedRemoteTimestamp = remoteTimestamp + task.getTimeDiffMillis() + task.getGranularityMillis(); StringBuilder msg; synchronized (TIMESTAMP_LOGGING_SDF) { msg = new StringBuilder(" [") .append(TIMESTAMP_LOGGING_SDF.format(new Date(localTimestamp))) .append("] local"); } task.log(msg.toString(), Project.MSG_VERBOSE); synchronized (TIMESTAMP_LOGGING_SDF) { msg = new StringBuilder(" [") .append(TIMESTAMP_LOGGING_SDF.format(new Date(adjustedRemoteTimestamp))) .append("] remote"); } if (remoteTimestamp != adjustedRemoteTimestamp) { synchronized (TIMESTAMP_LOGGING_SDF) { msg.append(" - (raw: ") .append(TIMESTAMP_LOGGING_SDF.format(new Date(remoteTimestamp))) .append(")"); } } task.log(msg.toString(), Project.MSG_VERBOSE); if (task.getAction() == FTPTask.SEND_FILES) { return adjustedRemoteTimestamp >= localTimestamp; } return localTimestamp >= adjustedRemoteTimestamp; } /** * Sends a site command to the ftp server * @param ftp ftp client * @param theCMD command to execute * @throws IOException in unknown circumstances * @throws BuildException in unknown circumstances */ protected void doSiteCommand(FTPClient ftp, String theCMD) throws IOException, BuildException { task.log("Doing Site Command: " + theCMD, Project.MSG_VERBOSE); if (!ftp.sendSiteCommand(theCMD)) { task.log("Failed to issue Site Command: " + theCMD, Project.MSG_WARN); } else { for (String reply : ftp.getReplyStrings()) { if (!reply.contains("200")) { task.log(reply, Project.MSG_WARN); } } } } /** * Sends a single file to the remote host. <code>filename</code> may * contain a relative path specification. When this is the case, <code>sendFile</code> * will attempt to create any necessary parent directories before sending * the file. The file will then be sent using the entire relative path * spec - no attempt is made to change directories. It is anticipated that * this may eventually cause problems with some FTP servers, but it * simplifies the coding. * @param ftp ftp client * @param dir base directory of the file to be sent (local) * @param filename relative path of the file to be send * locally relative to dir * remotely relative to the remotedir attribute * @throws IOException in unknown circumstances * @throws BuildException in unknown circumstances */ protected void sendFile(FTPClient ftp, String dir, String filename) throws IOException, BuildException { InputStream instream = null; try { // TODO - why not simply new File(dir, filename)? File file = task.getProject().resolveFile(new File(dir, filename).getPath()); if (task.isNewer() && isUpToDate(ftp, file, resolveFile(filename))) { return; } if (task.isVerbose()) { task.log("transferring " + file.getAbsolutePath()); } instream = new BufferedInputStream(Files.newInputStream(file.toPath())); createParents(ftp, filename); ftp.storeFile(resolveFile(filename), instream); if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { String s = "could not put file: " + ftp.getReplyString(); if (task.isSkipFailedTransfers()) { task.log(s, Project.MSG_WARN); skipped++; } else { throw new BuildException(s); } } else { // see if we should issue a chmod command if (task.getChmod() != null) { doSiteCommand(ftp, "chmod " + task.getChmod() + " " + resolveFile(filename)); } task.log("File " + file.getAbsolutePath() + " copied to " + task.getServer(), Project.MSG_VERBOSE); transferred++; } } finally { FileUtils.close(instream); } } /** * Delete a file from the remote host. * @param ftp ftp client * @param filename file to delete * @throws IOException in unknown circumstances * @throws BuildException if skipFailedTransfers is set to false * and the deletion could not be done */ protected void delFile(FTPClient ftp, String filename) throws IOException, BuildException { if (task.isVerbose()) { task.log("deleting " + filename); } if (!ftp.deleteFile(resolveFile(filename))) { String s = "could not delete file: " + ftp.getReplyString(); if (task.isSkipFailedTransfers()) { task.log(s, Project.MSG_WARN); skipped++; } else { throw new BuildException(s); } } else { task.log("File " + filename + " deleted from " + task.getServer(), Project.MSG_VERBOSE); transferred++; } } /** * Delete a directory, if empty, from the remote host. * @param ftp ftp client * @param dirname directory to delete * @throws IOException in unknown circumstances * @throws BuildException if skipFailedTransfers is set to false * and the deletion could not be done */ protected void rmDir(FTPClient ftp, String dirname) throws IOException, BuildException { if (task.isVerbose()) { task.log("removing " + dirname); } if (!ftp.removeDirectory(resolveFile(dirname))) { String s = "could not remove directory: " + ftp.getReplyString(); if (task.isSkipFailedTransfers()) { task.log(s, Project.MSG_WARN); skipped++; } else { throw new BuildException(s); } } else { task.log("Directory " + dirname + " removed from " + task.getServer(), Project.MSG_VERBOSE); transferred++; } } /** * Retrieve a single file from the remote host. <code>filename</code> may * contain a relative path specification. <p> * * The file will then be retrieved using the entire relative path spec - * no attempt is made to change directories. It is anticipated that this * may eventually cause problems with some FTP servers, but it simplifies * the coding.</p> * @param ftp the ftp client * @param dir local base directory to which the file should go back * @param filename relative path of the file based upon the ftp remote directory * and/or the local base directory (dir) * @throws IOException in unknown circumstances * @throws BuildException if skipFailedTransfers is false * and the file cannot be retrieved. */ protected void getFile(FTPClient ftp, String dir, String filename) throws IOException, BuildException { OutputStream outstream = null; try { File file = task.getProject().resolveFile(new File(dir, filename).getPath()); if (task.isNewer() && isUpToDate(ftp, file, resolveFile(filename))) { return; } if (task.isVerbose()) { task.log("transferring " + filename + " to " + file.getAbsolutePath()); } File pdir = file.getParentFile(); if (!pdir.exists()) { pdir.mkdirs(); } outstream = new BufferedOutputStream(Files.newOutputStream(file.toPath())); ftp.retrieveFile(resolveFile(filename), outstream); if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { String s = "could not get file: " + ftp.getReplyString(); if (task.isSkipFailedTransfers()) { task.log(s, Project.MSG_WARN); skipped++; } else { throw new BuildException(s); } } else { task.log( "File " + file.getAbsolutePath() + " copied from " + task.getServer(), Project.MSG_VERBOSE); transferred++; if (task.isPreserveLastModified()) { outstream.close(); outstream = null; FTPFile[] remote = ftp.listFiles(resolveFile(filename)); if (remote.length > 0) { FILE_UTILS.setFileLastModified(file, remote[0].getTimestamp() .getTime().getTime()); } } } } finally { FileUtils.close(outstream); } } /** * List information about a single file from the remote host. <code>filename</code> * may contain a relative path specification. <p> * * The file listing will then be retrieved using the entire relative path * spec - no attempt is made to change directories. It is anticipated that * this may eventually cause problems with some FTP servers, but it * simplifies the coding.</p> * @param ftp ftp client * @param bw buffered writer * @param filename the directory one wants to list * @throws IOException in unknown circumstances * @throws BuildException in unknown circumstances */ protected void listFile(FTPClient ftp, BufferedWriter bw, String filename) throws IOException, BuildException { if (task.isVerbose()) { task.log("listing " + filename); } FTPFile[] ftpfiles = ftp.listFiles(resolveFile(filename)); if (ftpfiles != null && ftpfiles.length > 0) { bw.write(ftpfiles[0].toString()); bw.newLine(); transferred++; } } /** * Create the specified directory on the remote host. * * @param ftp The FTP client connection * @param dir The directory to create (format must be correct for host * type) * @throws IOException in unknown circumstances * @throws BuildException if ignoreNoncriticalErrors has not been set to true * and a directory could not be created, for instance because it was * already existing. Precisely, the codes 521, 550 and 553 will trigger * a BuildException */ protected void makeRemoteDir(FTPClient ftp, String dir) throws IOException, BuildException { String workingDirectory = ftp.printWorkingDirectory(); if (task.isVerbose()) { if (dir.startsWith("/") || workingDirectory == null) { task.log("Creating directory: " + dir + " in /"); } else { task.log("Creating directory: " + dir + " in " + workingDirectory); } } if (dir.startsWith("/")) { ftp.changeWorkingDirectory("/"); } StringTokenizer st = new StringTokenizer(dir, "/"); while (st.hasMoreTokens()) { String subdir = st.nextToken(); task.log("Checking " + subdir, Project.MSG_DEBUG); if (!ftp.changeWorkingDirectory(subdir)) { if (!ftp.makeDirectory(subdir)) { // codes 521, 550 and 553 can be produced by FTP Servers // to indicate that an attempt to create a directory has // failed because the directory already exists. int rc = ftp.getReplyCode(); if (!task.isIgnoreNoncriticalErrors() || rc != CODE_550 && rc != CODE_553 && rc != CODE_521) { throw new BuildException("could not create directory: " + ftp.getReplyString()); } if (task.isVerbose()) { task.log("Directory already exists"); } } else { if (task.isVerbose()) { task.log("Directory created OK"); } ftp.changeWorkingDirectory(subdir); } } } if (workingDirectory != null) { ftp.changeWorkingDirectory(workingDirectory); } } /** * look at the response for a failed mkdir action, decide whether * it matters or not. If it does, we throw an exception * @param ftp current ftp connection * @throws BuildException if this is an error to signal */ private void handleMkDirFailure(FTPClient ftp) throws BuildException { int rc = ftp.getReplyCode(); if (!task.isIgnoreNoncriticalErrors() || rc != CODE_550 && rc != CODE_553 && rc != CODE_521) { throw new BuildException("could not create directory: " + ftp.getReplyString()); } } @Override public void doFTP() throws BuildException { FTPClient ftp = null; try { task.log("Opening FTP connection to " + task.getServer(), Project.MSG_VERBOSE); ftp = new FTPClient(); if (task.isConfigurationSet()) { ftp = FTPConfigurator.configure(ftp, task); } ftp.setRemoteVerificationEnabled(task.getEnableRemoteVerification()); ftp.connect(task.getServer(), task.getPort()); if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { throw new BuildException("FTP connection failed: %s", ftp.getReplyString()); } task.log("connected", Project.MSG_VERBOSE); task.log("logging in to FTP server", Project.MSG_VERBOSE); if ((task.getAccount() != null && !ftp.login(task.getUserid(), task.getPassword(), task.getAccount())) || (task.getAccount() == null && !ftp.login(task.getUserid(), task.getPassword()))) { throw new BuildException("Could not login to FTP server"); } task.log("login succeeded", Project.MSG_VERBOSE); if (task.isBinary()) { ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE); } else { ftp.setFileType(org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE); } if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { throw new BuildException("could not set transfer type: %s", ftp.getReplyString()); } if (task.isPassive()) { task.log("entering passive mode", Project.MSG_VERBOSE); ftp.enterLocalPassiveMode(); if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { throw new BuildException( "could not enter into passive mode: %s", ftp.getReplyString()); } } // If an initial command was configured then send it. // Some FTP servers offer different modes of operation, // E.G. switching between a UNIX file system mode and // a legacy file system. if (task.getInitialSiteCommand() != null) { final FTPClient lftp = ftp; executeRetryable(new RetryHandler(task.getRetriesAllowed(), task), () -> doSiteCommand(lftp, task.getInitialSiteCommand()), "initial site command: " + task.getInitialSiteCommand()); } // For a unix ftp server you can set the default mask for all files // created. if (task.getUmask() != null) { final FTPClient lftp = ftp; executeRetryable( new RetryHandler(task.getRetriesAllowed(), task), () -> doSiteCommand(lftp, "umask " + task.getUmask()), "umask " + task.getUmask()); } // If the action is MK_DIR, then the specified remote // directory is the directory to create. if (task.getAction() == FTPTask.MK_DIR) { final FTPClient lftp = ftp; executeRetryable( new RetryHandler(task.getRetriesAllowed(), task), () -> makeRemoteDir(lftp, task.getRemotedir()), task.getRemotedir()); } else if (task.getAction() == FTPTask.SITE_CMD) { final FTPClient lftp = ftp; executeRetryable( new RetryHandler(task.getRetriesAllowed(), task), () -> doSiteCommand(lftp, task.getSiteCommand()), "Site Command: " + task.getSiteCommand()); } else { if (task.getRemotedir() != null) { task.log("changing the remote directory", Project.MSG_VERBOSE); ftp.changeWorkingDirectory(task.getRemotedir()); if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { throw new BuildException( "could not change remote directory: %s", ftp.getReplyString()); } } if (task.isNewer() && task.isTimeDiffAuto()) { // in this case we want to find how much time span there is between local // and remote task.setTimeDiffMillis(getTimeDiff(ftp)); } task.log(FTPTask.ACTION_STRS[task.getAction()] + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()]); transferFiles(ftp); } } catch (IOException ex) { throw new BuildException("error during FTP transfer: " + ex, ex); } finally { if (ftp != null && ftp.isConnected()) { try { task.log("disconnecting", Project.MSG_VERBOSE); ftp.logout(); ftp.disconnect(); } catch (IOException ex) { // ignore it } } } } }
⏎ org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java
Or download all of them as a single archive file:
File name: apache-ant-1.10.10-fyi.zip File size: 2392938 bytes Release date: 2021-04-17 Download
⇐ Download Apache Ant Source Package
2021-07-10, 110951👍, 0💬
Popular Posts:
Java Cryptography Extension 1.2.2 JAR File Size and Download Location: File name: jce.jar, jce-1.2.2...
What Is mail.jar of JavaMail 1.4? I got the JAR file from javamail-1_4.zip. mail.jar in javamail-1_4...
JDK 11 java.management.jmod is the JMOD file for JDK 11 Management module. JDK 11 Management module ...
JDK 11 jdk.aot.jmod is the JMOD file for JDK 11 Ahead-of-Time (AOT) Compiler module. JDK 11 AOT Comp...
ASM is an all purpose Java bytecode manipulation and analysis framework. It can be used to modify ex...