/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.internal.soa.esb.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.log4j.Logger;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.addressing.eprs.FTPEpr;
import org.jboss.soa.esb.addressing.eprs.FileEpr;
import org.jboss.soa.esb.common.Environment;
import org.jboss.soa.esb.common.ModulePropertyManager;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.util.FileUtil;
import org.jboss.soa.esb.util.FtpClientUtil;
import org.jboss.soa.esb.util.RemoteFileSystem;
import org.jboss.soa.esb.util.RemoteFileSystemException;
import org.picketlink.identity.federation.api.openid.OpenIDManager.CONST;
/**
* Simplified FTP transfers
* <p>
* Description: Implements a simple set of FTP functionality Parameters to
* establish the FTP connection are provided at construction time and cannot
* change during the lifetime of the object <br/>Hides low level details.
* </p>
*/
public class FtpImpl implements RemoteFileSystem
{
private static Logger logger = Logger.getLogger(FtpImpl.class);
private static final String TMP_SUFFIX = ".rosettaPart";
private boolean m_bPassive;
private int m_iPort;
private int m_iTimeoutDefault, m_iTimeoutData, m_iTimeoutSo;
protected FTPClient m_oConn ;
private FTPEpr m_oEpr;
protected ConfigTree m_oParms;
private String m_sFtpServer, m_sUser, m_sPasswd;
private String m_sRemoteDir, m_sLocalDir;
private int m_iRenameRetry;
private String m_ControlChannelEncoding;
// package-protected since the only use is from FtpImplUnitTest
FtpImpl (ConfigTree p_oP, boolean p_bConnect)
throws ConfigurationException, RemoteFileSystemException
{
m_oParms = p_oP;
initialize(p_bConnect);
}
public FtpImpl (FTPEpr p_oP, boolean p_bConnect)
throws ConfigurationException, RemoteFileSystemException
{
this(p_oP) ;
configTreeFromEpr() ;
initialize(p_bConnect) ;
}
// protected since the only use is from "this" and FtpsImpl
protected FtpImpl (FTPEpr p_oP)
throws ConfigurationException
{
m_oEpr = p_oP;
final URI uri;
try
{
uri = m_oEpr.getURI();
}
catch (URISyntaxException e)
{
throw new ConfigurationException(e);
}
m_sFtpServer = uri.getHost();
String[] sa = null;
if (uri.getUserInfo() != null)
sa = uri.getUserInfo().split(":");
final int saLen = (sa == null ? 0 : sa.length) ;
switch(saLen)
{
case 1:
m_sUser = sa[0] ;
break;
case 2:
m_sUser = sa[0] ;
m_sPasswd = sa[1] ;
}
m_sRemoteDir = uri.getPath();
if ((m_sRemoteDir == null) || (m_sRemoteDir.equals("")))
m_sRemoteDir = FtpUtils.getRemoteDir();
m_iPort = uri.getPort();
m_sLocalDir = FtpUtils.getLocalDir();
File oLocalDir = new File(m_sLocalDir);
if(!oLocalDir.exists()) {
throw new ConfigurationException("Local FTP directory '" + oLocalDir.getAbsolutePath()
+ "' doesn't exist. Check your JBossESB config '"
+ ModulePropertyManager.TRANSPORTS_MODULE + ":" + Environment.FTP_LOCALDIR + "'");
}
m_bPassive = m_oEpr.getPassive();
m_iRenameRetry = FtpUtils.getRenameRetry();
m_iTimeoutDefault = FtpUtils.getTimeoutDefault();
m_iTimeoutData = FtpUtils.getTimeoutData();
m_iTimeoutSo = FtpUtils.getTimeoutSo();
m_ControlChannelEncoding = m_oEpr.getControlChannelEncoding();
}
/**
* Checks validity and completeness of parameters, and keeps the info
* internally for subsequent FTP requests
*
* @throws ConfigurationException :
* if parameters are invalid or incomplete
* <li>Parameters: (XML attributes at the root level) </li>
* <li> ftpServer = name or IP of FTP server </li>
* <li> ftpUser = login ID for server </li>
* <li> ftpPassword </li>
* <li> localDirURI = absolute path in the local filesystem
* </li>
* <li> remoteDirURI = remote path is relative to ftp user home
* in remote computer </li>
*/
protected void checkParms () throws ConfigurationException
{
String att = m_oParms.getAttribute(FileEpr.URL_TAG);
URI uri = null;
try
{
if (att != null)
uri = new URI(att);
}
catch (URISyntaxException ex)
{
throw new ConfigurationException(ex);
}
m_sFtpServer = (null != uri) ? uri.getHost() : m_oParms.getAttribute(PARMS_FTP_SERVER);
if (null == m_sFtpServer) {
throw new ConfigurationException("No FTP server specified");
}
String[] sa = (null == uri) ? null : uri.getUserInfo().split(":");
m_sUser = (null != sa) ? sa[0] : m_oParms.getAttribute(PARMS_USER);
if (null == m_sUser) {
throw new ConfigurationException("No username specified for FTP");
}
m_sPasswd = ((null != sa) && (sa.length > 1)) ? sa[1] : m_oParms.getAttribute(PARMS_PASSWD);
m_sRemoteDir = (null != uri) ? uri.getPath() : m_oParms.getAttribute(PARMS_REMOTE_DIR);
if (null == m_sRemoteDir) m_sRemoteDir = "";
m_sLocalDir = m_oParms.getAttribute(PARMS_LOCAL_DIR);
if (null == m_sLocalDir) m_sLocalDir = ".";
String sAux = m_oParms.getAttribute(PARMS_PORT);
m_iPort = (null != uri) ? uri.getPort() : (null == sAux) ? 21 : Integer.parseInt(sAux);
m_bPassive = false;
sAux = m_oParms.getAttribute(PARMS_PASSIVE);
m_bPassive = (null != sAux) && Boolean.parseBoolean(sAux);
m_ControlChannelEncoding = null;
String ccEncoding = m_oParms.getAttribute(RemoteFileSystem.PARMS_CONTROL_CHANNEL_ENCODING);
m_ControlChannelEncoding = (null != ccEncoding) ? ccEncoding : m_ControlChannelEncoding;
String sRenameRetry = m_oParms.getAttribute(PARMS_RENAME_RETRY);
m_iRenameRetry = (null != sRenameRetry) ? Integer.parseInt(sRenameRetry) : FtpUtils.getRenameRetry();
String sTimeoutDefault = m_oParms.getAttribute(PARMS_TIMEOUT_DEFAULT);
m_iTimeoutDefault = (null != sTimeoutDefault) ? Integer.parseInt(sTimeoutDefault) : FtpUtils.getTimeoutDefault();
String sTimeoutData = m_oParms.getAttribute(PARMS_TIMEOUT_DATA);
m_iTimeoutData = (null != sTimeoutData) ? Integer.parseInt(sTimeoutData) : FtpUtils.getTimeoutData();
String sTimeoutSo = m_oParms.getAttribute(PARMS_TIMEOUT_SO);
m_iTimeoutSo = (null != sTimeoutSo) ? Integer.parseInt(sTimeoutSo) : FtpUtils.getTimeoutSo();
}
protected void configTreeFromEpr () throws RemoteFileSystemException
{
m_oParms = new ConfigTree("fromEpr");
try
{
m_oParms.setAttribute(PARMS_FTP_SERVER, m_sFtpServer);
m_oParms.setAttribute(PARMS_USER, m_sUser);
if (m_sPasswd != null)
{
m_oParms.setAttribute(PARMS_PASSWD, m_sPasswd);
}
m_oParms.setAttribute(PARMS_REMOTE_DIR, m_sRemoteDir);
if (m_iPort > 0)
{
m_oParms.setAttribute(PARMS_PORT, Integer.toString(m_iPort));
}
m_oParms.setAttribute(RemoteFileSystem.PARMS_CONTROL_CHANNEL_ENCODING, this.m_ControlChannelEncoding);
m_oParms.setAttribute(PARMS_LOCAL_DIR, m_sLocalDir);
m_oParms.setAttribute(PARMS_ASCII, Boolean.toString(false));
m_oParms.setAttribute(PARMS_PASSIVE, Boolean.toString(m_bPassive));
m_oParms.setAttribute(PARMS_RENAME_RETRY, Integer.toString(m_iRenameRetry));
m_oParms.setAttribute(PARMS_TIMEOUT_DEFAULT, Integer.toString(m_iTimeoutDefault));
m_oParms.setAttribute(PARMS_TIMEOUT_DATA, Integer.toString(m_iTimeoutData));
m_oParms.setAttribute(PARMS_TIMEOUT_SO, Integer.toString(m_iTimeoutSo));
}
catch (Exception e)
{
throw new RemoteFileSystemException(e);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#deleteRemoteFile(java.lang.String)
*/
public void deleteRemoteFile (String p_sFile) throws RemoteFileSystemException
{
try
{
changeRemoteDirectory() ;
if (!m_oConn.deleteFile(p_sFile))
{
throw new RemoteFileSystemException("Failed to delete remote file: " + m_oConn.getReplyString());
}
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#downloadFile(java.lang.String,
* java.lang.String)
*/
public void downloadFile (String p_sFile, String p_sFinalName)
throws IOException, RemoteFileSystemException
{
try
{
final File to = new File(p_sFinalName) ;
final File oLocalDir = new File(m_sLocalDir);
final File oNew = (to.isAbsolute() ? to : new File(oLocalDir, p_sFinalName)) ;
if (oNew.exists()) {
boolean result = oNew.delete();
if (!result) {
logger.error("Could not delete file " + oNew.getAbsolutePath());
}
}
final File toTmp = new File(p_sFinalName + TMP_SUFFIX) ;
final File oNewTmp = (toTmp.isAbsolute() ? toTmp : new File(oLocalDir, p_sFinalName + TMP_SUFFIX)) ;
if (oNewTmp.exists()) {
boolean result = oNewTmp.delete();
if (!result) {
logger.error("Could not delete file " + oNewTmp.getAbsolutePath());
}
}
changeRemoteDirectory() ;
final InputStream is = m_oConn.retrieveFileStream(p_sFile) ;
if (is == null)
{
throw new RemoteFileSystemException("Could not download file: " + m_oConn.getReplyString());
}
try
{
final FileOutputStream fos = new FileOutputStream(oNewTmp) ;
try
{
copyStream(is, fos) ;
}
finally
{
fos.close() ;
}
}
finally
{
is.close() ;
}
if (!m_oConn.completePendingCommand())
{
boolean result = oNewTmp.delete();
if (!result) {
logger.error("Could not delete file " + oNewTmp.getAbsolutePath());
}
throw new RemoteFileSystemException("Failed to download contents: " + m_oConn.getReplyString()) ;
}
FileUtil.renameTo(oNewTmp, oNew) ;
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe) ;
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#getFileListFromRemoteDir(java.lang.String)
*/
public String[] getFileListFromRemoteDir (String p_sSuffix)
throws RemoteFileSystemException, IOException
{
if ((p_sSuffix == null) || ("".equals(p_sSuffix))) {
try
{
changeRemoteDirectory() ;
return m_oConn.listNames() ;
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe) ;
}
} else {
String sSuffix = "*" + p_sSuffix;
try
{
changeRemoteDirectory() ;
return m_oConn.listNames(sSuffix) ;
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe) ;
}
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#getRemoteDir()
*/
public String getRemoteDir ()
{
return m_sRemoteDir;
}
protected void initialize (boolean bConnect)
throws ConfigurationException, RemoteFileSystemException
{
checkParms();
m_oConn = instantiateClient() ;
if( m_ControlChannelEncoding != null ) {
m_oConn.setControlEncoding(this.m_ControlChannelEncoding);
}
if (bConnect)
{
try
{
if (m_iTimeoutDefault > 0)
{
m_oConn.setDefaultTimeout(m_iTimeoutDefault);
}
connect() ;
if (!m_oConn.isConnected())
throw new RemoteFileSystemException(
"Can't connect to FTP server");
if (!m_oConn.login(m_sUser, m_sPasswd))
{
m_oConn.logout() ;
throw new RemoteFileSystemException("Remote login failed: " + m_oConn.getReplyString());
}
m_oConn.setFileType(FTP.BINARY_FILE_TYPE) ;
if (m_bPassive)
{
m_oConn.enterLocalPassiveMode() ;
}
if (m_iTimeoutData > 0)
{
m_oConn.setDataTimeout(m_iTimeoutData);
}
if (m_iTimeoutSo > 0)
{
m_oConn.setSoTimeout(m_iTimeoutSo);
}
}
catch (final IOException ioe)
{
if (m_oConn.isConnected())
{
try
{
m_oConn.disconnect() ;
}
catch (final IOException ioe2) {} // ignore
}
throw new RemoteFileSystemException(ioe);
}
}
}
protected FTPClient instantiateClient()
throws RemoteFileSystemException
{
return new FTPClient() ;
}
protected void connect()
throws IOException
{
if (m_iPort > 0)
{
m_oConn.connect(m_sFtpServer, m_iPort) ;
}
else
{
m_oConn.connect(m_sFtpServer) ;
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#quit()
*/
public void quit ()
{
if (null != m_oConn)
{
try
{
m_oConn.quit();
m_oConn.disconnect() ;
}
catch (Exception e)
{
}
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#remoteDelete(java.io.File)
*/
public void remoteDelete (File p_oFile) throws RemoteFileSystemException
{
try
{
changeRemoteDirectory() ;
if (!m_oConn.deleteFile(p_oFile.getName()))
{
throw new RemoteFileSystemException("Failed to delete remote file: " + m_oConn.getReplyString());
}
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#remoteRename(java.io.File,
* java.io.File)
*/
public void remoteRename (File p_oFrom, File p_oTo) throws RemoteFileSystemException
{
try
{
changeRemoteDirectory();
if (!m_oConn.rename(FtpClientUtil.fileToFtpString(p_oFrom),
FtpUtils.fileToFtpString(p_oTo)))
{
throw new RemoteFileSystemException("Failed to rename file: " + m_oConn.getReplyString());
}
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#renameInRemoteDir(java.lang.String,
* java.lang.String)
*/
public void renameInRemoteDir (String p_sFrom, String p_sTo)
throws RemoteFileSystemException
{
try
{
changeRemoteDirectory() ;
if (!m_oConn.rename(p_sFrom, p_sTo))
{
throw new RemoteFileSystemException("Failed to rename file: " + m_oConn.getReplyString());
}
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#setRemoteDir(java.lang.String)
*/
public void setRemoteDir (String p_sDir) throws RemoteFileSystemException
{
if (p_sDir == null)
throw new IllegalArgumentException();
m_sRemoteDir = p_sDir;
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.util.RemoteFileSystem#uploadFile(java.io.File,
* java.lang.String)
*/
public void uploadFile (File p_oFile, String p_sRemoteName)
throws RemoteFileSystemException
{
try
{
changeRemoteDirectory() ;
final String sRemoteTmp = p_sRemoteName + TMP_SUFFIX;
final OutputStream os = m_oConn.storeFileStream(sRemoteTmp) ;
if (os == null)
{
throw new RemoteFileSystemException("Failed to obtain output stream: " + m_oConn.getReplyString()) ;
}
try
{
final FileInputStream fis = new FileInputStream(p_oFile) ;
try
{
copyStream(fis, os) ;
}
finally
{
fis.close() ;
}
}
finally
{
os.flush() ;
os.close() ;
}
if (!m_oConn.completePendingCommand())
{
throw new RemoteFileSystemException("Failed to upload contents: " + m_oConn.getReplyString()) ;
}
boolean retryProblem = true; // https://jira.jboss.org/jira/browse/JBESB-1995
for (int i = 0; i < m_iRenameRetry; i++)
{
if (m_oConn.rename(sRemoteTmp, p_sRemoteName))
{
retryProblem = false;
break;
}
else
{
if (i+1 < m_iRenameRetry)
{
try
{
Thread.sleep(1000L); // 1 second
}
catch (final Exception ex)
{
}
}
}
}
if (retryProblem)
throw new RemoteFileSystemException("Failed to rename file: " + m_oConn.getReplyString());
}
catch (final IOException ioe)
{
throw new RemoteFileSystemException(ioe);
}
}
private void changeRemoteDirectory()
throws IOException, RemoteFileSystemException
{
final String remoteDir = getRemoteDir() ;
if ((remoteDir != null) && (remoteDir.length() > 0))
{
if (!m_oConn.changeWorkingDirectory(remoteDir))
{
throw new RemoteFileSystemException("Failed to change to remote directory: " + m_oConn.getReplyString());
}
}
}
private void copyStream(final InputStream is, final OutputStream os)
throws IOException
{
final BufferedInputStream bis = new BufferedInputStream(is) ;
final BufferedOutputStream bos = new BufferedOutputStream(os) ;
final byte[] buffer = new byte[256] ;
while(true)
{
final int count = bis.read(buffer) ;
if (count <= 0)
{
break ;
}
bos.write(buffer, 0, count) ;
}
bos.flush() ;
}
}