/*
* 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.couriers;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.UUID;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.couriers.helpers.FileHandler;
import org.jboss.internal.soa.esb.couriers.helpers.FileHandlerFactory;
import org.jboss.internal.soa.esb.couriers.helpers.LocalFileHandler;
import org.jboss.internal.soa.esb.util.FtpUtils;
import org.jboss.soa.esb.addressing.Call;
import org.jboss.soa.esb.addressing.MalformedEPRException;
import org.jboss.soa.esb.addressing.eprs.FileEpr;
import org.jboss.soa.esb.couriers.CourierException;
import org.jboss.soa.esb.couriers.CourierMarshalUnmarshalException;
import org.jboss.soa.esb.couriers.CourierServiceBindException;
import org.jboss.soa.esb.couriers.CourierTimeoutException;
import org.jboss.soa.esb.couriers.CourierTransportException;
import org.jboss.soa.esb.couriers.CourierUtil;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.util.FileUtil;
import org.jboss.soa.esb.util.Util;
/**
* Internal implementation of a courier to handle Messages in a filesystem <p/>
* Intended to cater for local file system, remote via ftp, ftps or sftp
*
* @author <a
* href="mailto:schifest@heuristica.com.ar">schifest@heuristica.com.ar</a>
* @since Version 4.0
*
*/
public class FileCourier implements PickUpOnlyCourier, DeliverOnlyCourier
{
/**
* disable public default constructor
*/
protected FileCourier()
{
}
/**
* package protected constructor - Objects of this class should only be
* instantiated by internal implementations
*
* @param epr
*/
FileCourier(FileEpr epr) throws CourierException, MalformedEPRException
{
this(epr, false);
}
/**
* package protected constructor - Objects of this class should only be
* instantiated by internal implementations
*
* @param epr
* @param receiverOnly
*/
FileCourier(FileEpr epr, boolean receiverOnly) throws CourierException, MalformedEPRException
{
_receiverOnly = receiverOnly;
_epr = epr;
checkEprParms();
} // ________________________________
/**
* See if we have everything we need in the EPR
*
* @throws CourierException
*/
protected void checkEprParms() throws CourierException, MalformedEPRException
{
_inputSuffix = _epr.getInputSuffix();
_logger.debug("input suffix set to "+_inputSuffix);
// Certain things can only be checked in local filesystem
try
{
_uri = _epr.getURI();
FileHandler handler = FileHandlerFactory.getInstance()
.getFileHandler(_epr);
if (handler instanceof LocalFileHandler)
{
_localFhandler = (LocalFileHandler) handler;
File file = new File(_uri);
if ((!_receiverOnly) && (!file.isDirectory()))
throw new CourierException(
"File for deliverAsync EPR must be a directory (file name will be MessageID)");
File directory = (file.isDirectory()) ? file : file
.getParentFile();
if (null==directory)
directory = new File("");
if (!directory.canRead())
throw new CourierException("Can't read directory "
+ directory.toString());
// need to write even if it's readOnly - file will be renamed
// during xfer
if (!directory.canWrite())
throw new CourierException("Can't write in directory "
+ directory.toString());
return;
}
}
catch (Exception e)
{
throw new MalformedEPRException(e);
}
} // ________________________________
/**
* package the ESB message in a File
*
* @param message
* Message - the message to deliverAsync
* @return boolean - the result of the delivery
* @throws CourierException -
* if problems were encountered
*/
public boolean deliver(Message message) throws CourierException, MalformedEPRException
{
if (_receiverOnly)
throw new CourierException("This is a pickUp-only Courier");
if (null == message)
return false;
// FileHandler is durable only for local filesystem (see
// checkEprParms())
FileHandler handler = (null != _localFhandler) ? _localFhandler
: FileHandlerFactory.getInstance().getFileHandler(_epr);
if (null == handler)
throw new CourierServiceBindException(
"Can't find appropriate file handler for "
+ _uri.toASCIIString());
Call call = message.getHeader().getCall();
if (null==call)
message.getHeader().setCall(call=new Call());
try
{
if (null==call.getMessageID())
call.setMessageID(new URI(UUID.randomUUID().toString()));
}
catch (URISyntaxException e)
{
throw new MalformedEPRException("Problems with message header ",e);
}
File tmpFile = null;
if (handler instanceof LocalFileHandler)
{
try
{
File dir = new File(_uri);
String name = message.getHeader().getCall().getMessageID()
.toString();
name += _inputSuffix;
tmpFile = CourierUtil.messageToLocalFile(dir, message);
handler.renameFile(tmpFile, new File(dir, name));
return true;
}
catch (final CourierException e)
{
throw new CourierTransportException(e);
}
catch (final IOException e)
{
throw new CourierMarshalUnmarshalException(e);
}
catch (final ParserConfigurationException e) // it's no longer thrown so we can ignore it!
{
throw new CourierException(e);
}
}
tmpFile = null;
try
{
Method upload = handler.getClass().getMethod("uploadFile",
new Class[]
{ File.class });
String sDir = FtpUtils.getLocalDir();
File dir = new File(sDir);
String name = message.getHeader().getCall().getMessageID().toString();
name += _inputSuffix;
tmpFile = CourierUtil.messageToLocalFile(dir, message);
File messageFile = new File(dir, name);
FileUtil.renameTo(tmpFile, messageFile);
tmpFile = messageFile;
upload.invoke(handler, new Object[]
{ messageFile });
return true;
}
catch (final IOException ex)
{
throw new CourierMarshalUnmarshalException(ex);
}
catch (final Exception e)
{
throw new CourierException(e);
}
finally
{
if ((null != tmpFile) && !tmpFile.delete() && _logger.isDebugEnabled())
{
_logger.debug("Failed to delete file " + tmpFile.getAbsolutePath()) ;
}
}
} // ________________________________
public Message pickup(long millis) throws CourierException, CourierTimeoutException
{
Message result = null;
long limit = System.currentTimeMillis()
+ ((millis < 100) ? 100 : millis);
do
{
FileHandler handler = (null != _localFhandler) ? _localFhandler
: FileHandlerFactory.getInstance().getFileHandler(_epr);
File[] files = handler.getFileList();
if (null != files && files.length > 0)
{
File input = files[0];
File work = workFile(input);
handler.renameFile(input, work);
try
{
result = readOneMessage(handler, work);
}
catch (CourierException e)
{
_logger.debug("FileCourier.pickup caught exception during readOneMessage: "+e);
if (null == errorFile(input))
handler.deleteFile(work);
else
handler.renameFile(work, errorFile(input));
continue;
}
File done = postFile(input);
if (null == done)
handler.deleteFile(work);
else
handler.renameFile(work, done);
return result;
}
try
{
long lSleep = limit - System.currentTimeMillis();
if (_pollLatency < lSleep)
lSleep = _pollLatency;
if (lSleep > 0)
Thread.sleep(lSleep);
}
catch (InterruptedException e)
{
return null;
}
} while (System.currentTimeMillis() <= limit);
return null;
} // ________________________________
private Message readOneMessage(FileHandler handler, File work) throws CourierException
{
if (handler instanceof LocalFileHandler)
{
try
{
return CourierUtil.messageFromLocalFile(work);
}
catch (final FileNotFoundException ex)
{
throw new CourierTransportException(ex);
}
catch (final IOException ex)
{
throw new CourierMarshalUnmarshalException(ex);
}
catch (final CourierException ex)
{
throw ex;
}
catch (final Exception ex)
{
throw new CourierException(ex);
}
}
File tmpFile = null;
try
{
Method download = handler.getClass().getMethod("downloadFile",
new Class[]
{ File.class });
tmpFile = (File) download.invoke(handler, new Object[]
{ work });
return CourierUtil.messageFromLocalFile(tmpFile);
}
catch (FileNotFoundException ex)
{
throw new CourierTransportException(ex);
}
catch (IOException ex)
{
throw new CourierTransportException(ex);
}
catch (final CourierException ex)
{
throw ex;
}
catch (Exception e)
{
throw new CourierTransportException(e);
}
finally
{
if ((null != tmpFile) && !tmpFile.delete() && _logger.isDebugEnabled())
{
_logger.debug("Failed to delete file " + tmpFile.getAbsolutePath()) ;
}
}
} // ________________________________
protected File workFile(File input)
{
String sfx = _epr.getWorkSuffix();
if (Util.isNullString(sfx))
{
sfx = ".esbInProcess";
_logger
.debug("No valid work suffix found in EPR - using default of "
+ sfx);
}
return new File(input.toString()+ sfx);
} // ________________________________
protected File errorFile(File input)
{
try
{
if (_epr.getErrorDelete())
return null;
}
catch (Exception e)
{
_logger.warn("Problems in FileEpr", e);
}
String sfx = _epr.getErrorSuffix();
if (Util.isNullString(sfx))
{
sfx = ".esbERROR";
_logger
.debug("No valid work suffix found in EPR - using default of "
+ sfx);
}
return new File(input.toString() + sfx);
} // ________________________________
protected File postFile(File input)
{
try
{
// if (_epr instanceof FTPEpr || _epr.getPostDelete())
if (_epr.getPostDelete())
return null;
}
catch (Exception e)
{
_logger.warn("Problems in FileEpr", e);
}
String inputDir = new File(input.getAbsolutePath()).getParent();
if (inputDir == null)
{
_logger.debug("Could not get parent directory for "+input);
inputDir="";
}
String dir = _epr.getPostDirectory();
if (null==dir)
{
dir = inputDir;
_logger
.debug("No valid post process directory found in EPR - using same as input ("
+ dir + ")");
}
String sfx = _epr.getPostSuffix();
if (Util.isNullString(sfx))
{
if (dir == null) // means inputDir is also null!
{
/*
* Aarrghh! We should be able to return an exception, but
* can't. Not changing the signature at this stage. Post
* GA.
*
* Plus we shouldn't be assuming getParent never returns null
* in the first place.
*/
_logger.error("No way to determine post process directory. Will use a default relative to cwd.");
}
if (dir.equals(inputDir))
{
sfx = ".esbProcessed";
_logger
.debug("No valid post suffix found in EPR - using default of "
+ sfx);
}
}
return new File(dir, input.getName() + sfx);
} // ________________________________
public void setPollLatency(Long millis)
{
if (millis <= 900)
_logger.warn("Poll latency must be >= 900 milliseconds - Keeping old value of "+_pollLatency);
else
_pollLatency = millis;
} // ________________________________
public void cleanup()
{
}
protected long _pollLatency = 900;
protected String _inputSuffix;
protected URI _uri;
protected boolean _receiverOnly;
protected FileEpr _epr;
protected LocalFileHandler _localFhandler;
protected final static Logger _logger = Logger.getLogger(FileCourier.class);
} // ____________________________________________________________________________