package com.arrizza.ant.taskdefs;
//import java.net.*;
import com.oroinc.net.ftp.*;
import java.io.*;
import java.util.*;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.*;
//----------------------------------------------
public class FtpMirror extends Task implements ILogger
{
protected FtpOptions myFtpOptions = new FtpOptions();
protected Options myOptions = new Options();
private FTPWrapper myFtpWrapper = new FTPWrapper(myOptions, myFtpOptions, this);
//----------------------------------------------
protected void UT_setFtpClient(FtpClient theMock)
{
myFtpWrapper.UT_setFtpClient(theMock);
}
//----------------------------------------------
public void execute() //throws BuildException
{
checkConfiguration();
try
{
myFtpWrapper.initialize();
mirror();
}
catch (FtpException ex)
{
throw new BuildException(ex.getMessage());
}
catch (IOException ex)
{
throw new BuildException("unknown error during FTP transfer: " + ex);
}
finally
{
myFtpWrapper.cleanup();
}
}
//---------------------------------
public void setServer(String server)
{
myFtpOptions.server = server;
}
//---------------------------------
public void setPort(int port)
{
myFtpOptions.port = port;
}
//---------------------------------
public void setUserid(String userid)
{
myFtpOptions.userid = userid;
}
//---------------------------------
public void setPassword(String password)
{
myFtpOptions.password = password;
}
//---------------------------------
// Sets the remote file separator character. This normally defaults to
// the Unix standard forward slash, but can be manually overridden using
// this call if the remote server requires some other separator. Only
// the first character of the string is used.
public void setSeparator(String separator)
{
myFtpOptions.remoteFileSep = separator;
}
//---------------------------------
// Specifies whether to use binary-mode or text-mode transfers. Set
// to true to send binary mode. Binary mode is enabled by default.
public void setBinary(boolean binary)
{
myFtpOptions.binary = binary;
}
//---------------------------------
// Specifies whether to use passive mode. Set to true if you
// are behind a firewall and cannot connect without it. Passive mode
// is disabled by default.
public void setPassive(boolean passive)
{
myFtpOptions.passive = passive;
}
//---------------------------------
// set the flag to skip errors on dir creation (and maybe later other server specific errors)
public void setContinueOnError(boolean continueOnError)
{
myFtpOptions.continueOnError = continueOnError;
}
//---------------------------------
// if true, no FTP actions are done that are permanent, only read only
public void setShow(boolean show)
{
myFtpOptions.displayOnly = show;
}
//-----------------------------------------------------------
//-- private or protected from here on
//-----------------------------------------------------------
//---------------------------------
public void logInfo(String msg)
{
log(msg, Project.MSG_VERBOSE);
}
//---------------------------------
public void logWarning(String msg)
{
log(msg, Project.MSG_WARN);
}
//-----------------------------------------------------------
//-- set to true to transmit all files.
//-- Set to false to transmit only files that are new or changed from their
//-- remote counterparts.
//-- The default is to send only new/changed files (false).
public void setForceSend(boolean force)
{
myOptions.forceSend = force;
}
//-----------------------------------------------------------
//-- force the conversion to upper or lower case
//-- valid values:
//-- "upper" : convert remote paths to upper case
//-- "lower" : conver remote paths to lower case
//-- "" or null : leave as they are
public void setCase(String theCase)
{
myOptions.forceCase = theCase;
}
//@@@@@@@@@@@@@@@@@@ OLD FROM HERE ON @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
protected FileSet myLocalFileset = null;
protected Statistics stats = new Statistics();
public void addLocal(FileSet set)
{
if (myLocalFileset != null)
throw new BuildException("only one local fileset allowed");
myLocalFileset = set;
}
protected FileSet remoteFileset = null;
public void addRemote(FileSet set)
{
if (remoteFileset != null)
throw new BuildException("only one remote fileset allowed");
remoteFileset = set;
}
/**
* Runs the task.
*/
//-----------------------------------------------------------
//-- private or protected from here on
//-----------------------------------------------------------
//-----------------------------------------------------------
protected void checkConfiguration() //throws BuildException
{
myFtpOptions.check();
if (remoteFileset == null || myLocalFileset == null)
throw new BuildException("you must specify both a local and a remote fileset.");
if (remoteFileset.getDir(getProject()) == null)
throw new BuildException("you must specify a dir attribute on the remote fileset.");
if (myLocalFileset.getDir(getProject()) == null)
throw new BuildException("you must specify a dir attribute on the local fileset.");
}
//-----------------------------------------------------------
//-- mirror.
protected void mirror() throws FtpException, IOException //BuildException
{
MyScanners scanners = new MyScanners(getProject(), myLocalFileset, remoteFileset);
String lroot = myLocalFileset.getDir(getProject()).getCanonicalPath();
RemoteFileList rfilelist = new RemoteFileList(myFtpWrapper, scanners, getProject());
LocalFileList lfilelist = new LocalFileList(myFtpWrapper, scanners, lroot);
logInfo("mirror local: " + lroot + " remote: " + rfilelist.getRoot());
Stack ldirs = new Stack();
ldirs.push(".");
while (!ldirs.empty())
{
String lrelpath = (String) ldirs.pop();
lfilelist.setRelativePath(lrelpath);
rfilelist.setRelativePath(lrelpath);
lfilelist.getFiles(ldirs);
if (lfilelist.empty())
continue;
rfilelist.getFiles();
lfilelist.transferFiles(rfilelist);
rfilelist.cleanFiles();
}
}
//-------------
private String resolveLocalFile(String file)
{
return file.replace(myFtpOptions.remoteFileSep.charAt(0), System.getProperty("file.separator").charAt(0));
}
//-------------
private boolean isEitherNotIncluded(MyScanners scanners, String pfx, String fname, String relpath)
{
String fpath;
if (relpath.equals("."))
fpath = fname;
else
fpath = resolveLocalFile(relpath + "/" + fname);
return scanners.isEitherNotIncluded(pfx, fpath);
}
//-------------
private boolean isNotIncluded(MyDirectoryScannerBase scanner, String pfx, String fname, String relpath)
{
String fpath;
if (relpath.equals("."))
fpath = fname;
else
fpath = resolveLocalFile(relpath + "/" + fname);
return scanner.isNotIncluded(pfx, fpath);
}
//---------------------------------
public class Statistics
{
public int skipped = 0;
public int transferred = 0;
}
//---------------------------------
abstract class MyDirectoryScannerBase extends DirectoryScanner
{
public void checkConfig()
{
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];
}
}
protected boolean isNotIncluded(String pfx, String path)
{
boolean excluded = isExcluded(path) || !isIncluded(path);
if (excluded)
logInfo(" excluded: " + pfx + path);
return excluded;
}
}
//---------------------------------
class MyFTPDirectoryScanner extends MyDirectoryScannerBase
{
public MyFTPDirectoryScanner()//FTPClient ftp)
{
super();
}
}
//---------------------------------
class MyDirectoryScanner extends MyDirectoryScannerBase
{
public MyDirectoryScanner()
{
super();
}
}
//---------------------------------
class MyScanners
{
public MyFTPDirectoryScanner rds;
public MyDirectoryScanner lds;
public MyScanners(Project project, FileSet lfs, FileSet rfs)
{
lds = new MyDirectoryScanner();
lfs.setupDirectoryScanner(lds, project);
lds.checkConfig();
rds = new MyFTPDirectoryScanner();
rfs.setupDirectoryScanner(rds, project);
rds.checkConfig();
}
public boolean isEitherNotIncluded(String pfx, String path)
{
if (lds.isNotIncluded(pfx, path))
return true;
return rds.isNotIncluded(pfx, path);
}
}
//-----------------------------------------------------------
class RemoteFileList
{
private MyScanners scanners;
private String root;
private String homedir;
private String relpath;
private FTPWrapper ftpwrapper;
private FTPFile files[];
//-------------
public RemoteFileList(FTPWrapper ftpwrapper, MyScanners scanners, Project project) throws IOException
{
this.scanners = scanners;
this.ftpwrapper = ftpwrapper;
String basedir = project.getBaseDir().getCanonicalPath();
String tmp = remoteFileset.getDir(project).getCanonicalPath();
this.root = ftpwrapper.resolveRemoteFile(tmp.substring(basedir.length() + 1));
homedir = "/";
}
//-------------
public String getRoot()
{
return root;
}
//-------------
public void setRelativePath(String relpath)
{
this.relpath = relpath;
}
//-------------
public void getFiles() throws FtpException, IOException
{
files = null;
if (scanners.rds.isNotIncluded("remote dir =", relpath))
return ;
String rdirname = ftpwrapper.resolveRemoteFile(homedir + root + "/" + relpath);
files = ftpwrapper.ListFiles(rdirname);
if (files == null)
return ;
for (int i = 0; i < files.length; ++i)
{
FTPFile file = files[i];
String fname = file.getName();
String pfx = (String) (file.isDirectory() ? "dir =" : "file=");
if (isNotIncluded(scanners.rds, "remote " + pfx, fname, relpath))
{
files[i] = null;
continue;
}
logInfo(" included: remote " + pfx + fname);
}
}
//-------------
public void cleanFiles() throws FtpException, IOException
{
for (int i = 0; files != null && i < files.length; ++i)
{
FTPFile file = files[i];
if (file == null)
continue;
String fname = file.getName();
if (isEitherNotIncluded(scanners, "-- clean ", fname, relpath))
continue;
if (file.isDirectory())
ftpwrapper.Rmdir(fname);
else
ftpwrapper.Delete(fname);
}
}
//-------------
public FTPFile findFile(String fname)
{
if (files == null)
return null;
FTPFile rfile = null;
int j;
for (j = 0; j < files.length; ++j)
{
if (files[j] == null)
continue;
if (fname.equals(files[j].getName()))
{
rfile = files[j];
files[j] = null;
break;
}
}
return rfile;
}
public void transferFile(File lfile) throws FtpException, IOException
{
FTPFile rfile = findFile(lfile.getName());
if (rfile == null)
createNew(lfile);
else
overwriteExisting(lfile, rfile);
}
//-------------
private void createNew(File lfile) throws FtpException, IOException
{
if (lfile.isDirectory())
ftpwrapper.Mkdir(lfile.getName());
else
ftpwrapper.Send(lfile, null);
}
//-------------
private void overwriteExisting(File lfile, FTPFile rfile) throws FtpException, IOException
{
if (lfile.isDirectory() && rfile.isFile())
{
ftpwrapper.Delete(rfile.getName());
ftpwrapper.Mkdir(lfile.getName());
}
else if (lfile.isFile() && rfile.isDirectory())
{
ftpwrapper.Rmdir(rfile.getName());
ftpwrapper.Send(lfile, null);
}
else if (lfile.isFile() && rfile.isFile())
{
ftpwrapper.Send(lfile, rfile);
}
}
}
//-----------------------------------------------------------
protected class LocalFileList
{
private FTPWrapper ftpwrapper;
private MyScanners scanners;
private String root;
private String relpath;
private File files[];
//-------------
public LocalFileList(FTPWrapper ftpwrapper, MyScanners scanners, String root)
{
this.scanners = scanners;
this.root = root;
this.ftpwrapper = ftpwrapper;
this.files = null;
}
//-------------
public void setRelativePath(String relpath)
{
this.relpath = relpath;
}
//-------------
public boolean empty()
{
return files == null;
}
//-------------
public void getFiles(Stack ldirs) throws IOException
{
files = null;
if (scanners.lds.isNotIncluded("dir =", relpath))
return ;
String ldirname = resolveLocalFile(root + "/" + relpath);
File ldir = new File(ldirname);
files = ldir.listFiles();
logInfo(" included: local dir =" + relpath + " (#files=" + files.length + ")");
if (files == null)
{
return ;
}
for (int i = 0; i < files.length; ++i)
{
File file = files[i];
String fname = file.getName();
String pfx = (String) (file.isDirectory() ? "dir =" : "file=");
if (isNotIncluded(scanners.lds, " local " + pfx, fname, relpath))
{
files[i] = null;
continue;
}
logInfo(" included: local " + pfx + fname);
if (file.isDirectory())
saveNextPath(file, ldirs);
}
}
//-------------
public void transferFiles(RemoteFileList rfilelist) throws FtpException, IOException
{
for (int i = 0; i < files.length; ++i)
{
File lfile = files[i];
if (lfile == null)
continue;
if (isEitherNotIncluded(scanners, "-- xfer ", lfile.getName(), relpath))
continue;
rfilelist.transferFile(lfile);
}
}
//-------------
private void saveNextPath(File lfile, Stack ldirs) throws IOException
{
String fullpath = resolveLocalFile(lfile.getCanonicalPath());
String nextrelpath = fullpath.substring(root.length() + 1);
ldirs.push(nextrelpath);
}
}
}
//@@@@@@@@@@@@@@@@@@ OLD ABOVE HERE @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//----------------------------------------------
interface ILogger
{
void logInfo(String msg);
void logWarning(String msg);
}
//----------------------------------------------
class FtpException extends Exception
{
public String Message = "";
public FtpException(String msg)
{
Message = msg;
}
}
//---------------------------------
class FtpOptions
{
public String server = null;
public String userid = null;
public String password = null;
public int port = 21;
public String remoteFileSep = "/";
public boolean binary = true;
public boolean passive = false;
public boolean displayOnly = false;
public boolean continueOnError = false;
void check() // throws BuildException
{
if (server == null)
throw new BuildException("server attribute must be set!");
if (userid == null)
throw new BuildException("userid attribute must be set!");
if (password == null)
throw new BuildException("password attribute must be set!");
}
}
//---------------------------------
class Options
{
public boolean forceSend = false;
public String forceCase = "";
}
//---------------------------------
class FTPWrapper
{
Options myOptions;
FtpOptions myFtpOptions;
ILogger myLogger;
FtpClient myFtpClient = null;
//-------------
public FTPWrapper(Options theOptions, FtpOptions theFtpOptions, ILogger theLogger)
{
this.myOptions = theOptions;
this.myFtpOptions = theFtpOptions;
this.myLogger = theLogger;
}
//----------------------------------------------
public void UT_setFtpClient(FtpClient theMock)
{
myFtpClient = theMock;
}
//----------------------------------------------
public void initialize() throws FtpException
{
if (myFtpClient == null)
myFtpClient = new FtpClient();
myFtpClient.setLogger(myLogger);
myFtpClient.setOptions(myFtpOptions);
myFtpClient.connect();
myFtpClient.logon();
myFtpClient.setTransferAttributes();
}
//-------------
public FTPFile[] ListFiles(String rdirname) throws FtpException, IOException
{
myFtpClient.chdir(rdirname);
if (myFtpClient.isOk())
return myFtpClient.listFiles();
myLogger.logInfo("could not change to " + rdirname + ": " + myFtpClient.getReplyString());
Mkdir(rdirname);
return null;
}
//-------------
public void Mkdir(String rdirname) throws FtpException, IOException
{
myLogger.logInfo("-- mkdir " + rdirname);
myFtpClient.mkdir(rdirname);
}
//-------------
public void Rmdir(String rdirname) throws FtpException
{
myLogger.logInfo("-- rmdir " + rdirname);
myFtpClient.rmdir(rdirname);
}
//-------------
public void Delete(String rfilename) throws FtpException
{
myLogger.logInfo("-- del " + rfilename);
myFtpClient.delete(rfilename);
}
//-------------
public void Send(File lfile, FTPFile rfile) throws FtpException, IOException
{
if (rfile == null)
{
myLogger.logInfo("-- new " + lfile.getCanonicalPath());
myFtpClient.send(lfile);
}
else if (myOptions.forceSend)
{
myLogger.logInfo("-- force " + lfile.getCanonicalPath());
myFtpClient.send(lfile);
}
else if (lfile.lastModified() > rfile.getTimestamp().getTime().getTime())
{
myLogger.logInfo("-- chgd " + lfile.getCanonicalPath());
myFtpClient.send(lfile);
}
else
myLogger.logInfo("-- ok " + lfile.getCanonicalPath());
}
//-------------
public void cleanup()
{
myFtpClient.cleanup();
}
public String resolveRemoteFile(String file)
{
return myFtpClient.resolveRemoteFile(file);
}
}
//----------------------------------------------
class FtpClient extends FTPClient //note upper case!
{
ILogger myLogger;
FtpOptions myFtpOptions;
//----------------------------------------------
public void setLogger(ILogger log)
{
myLogger = log;
}
//----------------------------------------------
public void setOptions(FtpOptions theOptions)
{
myFtpOptions = theOptions;
}
//----------------------------------------------
public void connect() throws FtpException
{
final String cMethod = "FtpClient.connect(): ";
myLogger.logInfo("Opening FTP connection to " + myFtpOptions.server);
try
{
super.connect(myFtpOptions.server, myFtpOptions.port);
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
checkReturnCode(cMethod);
myLogger.logInfo("Connected");
}
//----------------------------------------------
public void logon() throws FtpException
{
final String cMethod = "FtpClient.logon(): ";
myLogger.logInfo("logging on to FTP server");
try
{
super.login(myFtpOptions.userid, myFtpOptions.password);
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
checkReturnCode(cMethod);
myLogger.logInfo("logon succeeded");
}
//----------------------------------------------
public void setTransferAttributes() throws FtpException
{
if (myFtpOptions.binary)
setBinaryMode();
if (myFtpOptions.passive)
setPassiveMode();
}
//----------------------------------------------
public void cleanup()
{
if (!isConnected()) return ;
myLogger.logInfo("disconnecting...");
try
{
super.logout();
super.disconnect();
}
catch (IOException ex)
{
// ignore it
}
myLogger.logInfo("disconnected");
}
//----------------------------------------------
public void mkdir(String dir) throws FtpException
{
if (myFtpOptions.displayOnly) return ;
makeRemoteDir(dir);
}
//----------------------------------------------
public void rmdir(String dir) throws FtpException
{
if (myFtpOptions.displayOnly) return ;
removeRemoteDir(dir);
}
//----------------------------------------------
public void chdir(String dir) throws FtpException
{
final String cMethod = "FtpClient.chdir(): ";
try
{
super.changeWorkingDirectory(dir);
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
}
//----------------------------------------------
public void delete(String fname) throws FtpException
{
if (myFtpOptions.displayOnly) return ;
delFile(fname);
}
//----------------------------------------------
public void send(File file) throws FtpException
{
if (myFtpOptions.displayOnly) return ;
sendFile(file);
}
//----------------------------------------------
public boolean isOk()
{
return FTPReply.isPositiveCompletion(super.getReplyCode());
}
//----------------------------------------------
//-- Private from here on
//----------------------------------------------
//----------------------------------------------
private void checkReturnCode(final String theMethod) throws FtpException
{
if (isOk()) return ;
throw new FtpException(theMethod + super.getReplyString());
}
//----------------------------------------------
private void setBinaryMode() throws FtpException
{
final String cMethod = "FtpClient.setBinaryMode(): ";
try
{
super.setFileType(com.oroinc.net.ftp.FTP.IMAGE_FILE_TYPE);
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
checkReturnCode(cMethod);
myLogger.logInfo("Binary mode is set");
}
//----------------------------------------------
private void setPassiveMode() throws FtpException
{
final String cMethod = "FtpClient.setPassiveMode(): ";
super.enterLocalPassiveMode();
checkReturnCode(cMethod);
myLogger.logInfo("Passive mode is set");
}
//----------------------------------------------
// Create the specified directory on the remote host.
private void makeRemoteDir(String dir ) throws FtpException
{
final String cMethod = "FtpClient.makeRemoteDir(): ";
myLogger.logInfo(" creating directory: " + dir);
boolean brc = false;
try
{
brc = super.makeDirectory(dir);
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
if (brc)
myLogger.logInfo(" directory created OK");
else
checkMkdirReturnCode(cMethod);
}
//----------------------------------------------
private void checkMkdirReturnCode(final String theMethod) throws FtpException
{
// Both codes 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 = super.getReplyCode();
if ( rc == 550 && rc == 553 && myFtpOptions.continueOnError)
{
myLogger.logWarning( " directory not created: " + super.getReplyString());
return ;
}
throw new FtpException( "could not create directory: " + super.getReplyString() );
}
//----------------------------------------------
// Remove the specified directory on the remote host.
private void removeRemoteDir(String dir) throws FtpException
{
final String cMethod = "FtpClient.removeRemoteDir(): ";
myLogger.logInfo(" removing directory: " + dir);
String cwd = getcwd();
delTree(dir);
chdir(cwd);
}
//----------------------------------------------
private String getcwd() throws FtpException
{
final String cMethod = "FtpClient.getcwd(): ";
try
{
return super.printWorkingDirectory();
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
}
//----------------------------------------------
private void delTree(String dir) throws FtpException
{
final String cMethod = "FtpClient.delTree(): ";
myLogger.logInfo(" delTree: cwd '" + dir + "'");
chdir(dir);
try
{
removeList(dir, super.listFiles());
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
removeParentDirectory(dir);
}
//----------------------------------------------
private void removeList(String dir, FTPFile[] files) throws FtpException
{
if (files == null) return ;
myLogger.logInfo( " delTree: list files (" + dir + ") : " + files.length + " files");
for (int i = 0; i < files.length; ++i)
{
removeEntity(dir, files[i]);
}
}
//----------------------------------------------
private void removeEntity(String dir, FTPFile file) throws FtpException
{
if (file.isDirectory())
delTree(file.getName());
else
delFile(file.getName());
}
//----------------------------------------------
private void delFile(String fname) throws FtpException
{
final String cMethod = "FtpClient.delFile(): ";
try
{
super.deleteFile(fname);
}
catch (IOException ex)
{
if (myFtpOptions.continueOnError)
myLogger.logWarning(cMethod + "error occurred: " + ex.getMessage());
else
throw new FtpException(cMethod + ex.getMessage());
}
myLogger.logInfo( " delFile: (" + fname + ")");
}
//----------------------------------------------
private void removeParentDirectory(String dir) throws FtpException
{
final String cMethod = "FtpClient.removeParentDirectory(): ";
try
{
super.changeToParentDirectory();
myLogger.logInfo( " delTree: chg parent (" + dir + ")");
super.removeDirectory(dir);
myLogger.logInfo( " delTree: removedir (" + dir + ")");
}
catch (IOException ex)
{
throw new FtpException(cMethod + ex.getMessage());
}
}
//-----------------------------------------------------
private void sendFile(File file) throws FtpException
{
final String cMethod = "FtpClient.sendFile(): ";
InputStream instream = null;
try
{
myLogger.logInfo(" sending " + file.getCanonicalPath());
instream = new BufferedInputStream(new FileInputStream(file));
super.storeFile(resolveRemoteFile(file.getName()), instream);
checkSendReturnCode(cMethod, file.getCanonicalPath());
}
catch (IOException ex)
{
myLogger.logInfo(" sendFile: threw an exception: " + ex.getMessage());
}
finally
{
closeStream(instream);
}
}
//----------------------------------------------
private void closeStream(InputStream instream)
{
if (instream == null) return ;
try
{
instream.close();
}
catch (IOException ex)
{
// ignore it
}
}
//----------------------------------------------
private void checkSendReturnCode(String cMethod, String filepath) throws FtpException
{
if (isOk())
{
myLogger.logInfo(" File " + filepath + " copied to " + myFtpOptions.server);
// stats.transferred++;
return ;
}
String s = cMethod + "error occurred: " + super.getReplyString();
if (myFtpOptions.continueOnError)
{
myLogger.logWarning(s);
// stats.skipped++;
}
else
{
throw new FtpException(s);
}
}
//----------------------------------------------
// 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.
public String resolveRemoteFile(String file)
{
return file.replace(System.getProperty("file.separator").charAt(0), myFtpOptions.remoteFileSep.charAt(0));
}
}
|