/**
* Copyright (C) 2012 Orbeon, Inc.
*
* This program 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 program 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.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.processor.file;
import org.apache.log4j.Logger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.optional.ssh.Scp;
import org.orbeon.dom.Document;
import org.orbeon.dom.Element;
import org.orbeon.oxf.common.OXFException;
import org.orbeon.oxf.pipeline.api.PipelineContext;
import org.orbeon.oxf.processor.ProcessorImpl;
import org.orbeon.oxf.processor.ProcessorInputOutputInfo;
import org.orbeon.oxf.processor.ProcessorUtils;
import org.orbeon.oxf.processor.serializer.FileSerializer;
import org.orbeon.oxf.util.LoggerFactory;
import org.orbeon.oxf.util.NetUtils;
import org.orbeon.oxf.xml.XPathUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Iterator;
/**
* The File Processor allows performing some operations on files.
*
* For now, just allow for one "delete" and "scp" operations.
*
* In the future, this can be enhanced to support multiple actions, including delete, create, "touch", rename, move,
* etc.
*/
public class FileProcessor extends ProcessorImpl {
private static final boolean DEFAULT_MAKE_DIRECTORIES = false;
private static Logger logger = LoggerFactory.createLogger(FileProcessor.class);
public static final String FILE_PROCESSOR_CONFIG_NAMESPACE_URI = "http://orbeon.org/oxf/xml/file-processor-config";
public FileProcessor() {
addInputInfo(new ProcessorInputOutputInfo(INPUT_CONFIG, FILE_PROCESSOR_CONFIG_NAMESPACE_URI));
}
public void start(PipelineContext context) {
try {
// Read config
final Document config = readCacheInputAsDOM4J(context, INPUT_CONFIG);
for (Iterator i = XPathUtils.selectNodeIterator(config, "/*/*"); i.hasNext();) {
final Element currentElement = (Element) i.next();
if (currentElement.getName().equals("delete")) {
// delete operation
// Get file object
final File file = NetUtils.getFile(
getDirectory(currentElement, "directory"),
XPathUtils.selectStringValueNormalize(currentElement, "file"),
XPathUtils.selectStringValueNormalize(currentElement, "url"),
getLocationData(),
false
);
// Delete file if it exists
if (file.exists() && file.canWrite()) {
final boolean deleted = file.delete();
if (!deleted)
throw new OXFException("Can't delete file: " + file);
}
} else if (currentElement.getName().equals("move")) {
// Move operation
// From
final File fromFile = NetUtils.getFile(
getDirectory(currentElement, "from/directory"),
XPathUtils.selectStringValueNormalize(currentElement, "from/file"),
XPathUtils.selectStringValueNormalize(currentElement, "from/url"),
getLocationData(),
false
);
if (!fromFile.exists() || ! fromFile.canRead()) {
throw new OXFException("Can't move file: " + fromFile);
}
// To
final File toFile = NetUtils.getFile(
getDirectory(currentElement, "to/directory"),
XPathUtils.selectStringValueNormalize(currentElement, "to/file"),
XPathUtils.selectStringValueNormalize(currentElement, "to/url"),
getLocationData(),
ProcessorUtils.selectBooleanValue(currentElement, "to/make-directories", DEFAULT_MAKE_DIRECTORIES)
);
if (! (toFile.exists() || toFile.createNewFile() )) {
throw new OXFException("Can't create file: " + toFile);
}
// Move
if (! fromFile.renameTo(toFile)) {
// If for whatever reason renameTo fails, try to copy and delete it
copyFile(fromFile, toFile);
final boolean deleted = fromFile.delete();
if (!deleted)
throw new OXFException("Can't delete file " + fromFile + " after copying it to " + toFile);
}
} else if (currentElement.getName().equals("copy")) {
// Copy operation
// From
final File fromFile = NetUtils.getFile(
getDirectory(currentElement, "from/directory"),
XPathUtils.selectStringValueNormalize(currentElement, "from/file"),
XPathUtils.selectStringValueNormalize(currentElement, "from/url"),
getLocationData(),
false
);
if (!fromFile.exists() || ! fromFile.canRead()) {
throw new OXFException("Can't copy file: " + fromFile);
}
// To
final File toFile = NetUtils.getFile(
getDirectory(currentElement, "to/directory"),
XPathUtils.selectStringValueNormalize(currentElement, "to/file"),
XPathUtils.selectStringValueNormalize(currentElement, "to/url"),
getLocationData(),
ProcessorUtils.selectBooleanValue(currentElement, "to/make-directories", DEFAULT_MAKE_DIRECTORIES)
);
if (! (toFile.exists() || toFile.createNewFile() )) {
throw new OXFException("Can't create file: " + toFile);
}
// Copy
copyFile(fromFile, toFile);
} else if (currentElement.getName().equals("scp")) {
// scp operation
// Create ant task
final Scp scp = new Scp() {
@Override
public void log(String msg, int msgLevel) {
switch (msgLevel) {
case Project.MSG_ERR:
logger.error(msg);
break;
case Project.MSG_WARN:
logger.warn(msg);
break;
case Project.MSG_INFO:
logger.info(msg);
break;
case Project.MSG_VERBOSE:
case Project.MSG_DEBUG:
logger.debug(msg);
break;
}
}
private final Project project = new Project() {
@Override
public File getBaseDir() {
return super.getBaseDir();
}
};
@Override
public Project getProject() {
return project;
}
};
scp.init();
// Set it up
setupScp(scp, currentElement);
// Execute it
scp.execute();
}
}
} catch (Exception e) {
throw new OXFException(e);
}
}
private String getDirectory(Element currentElement, String elementPath) {
final String configDirectory = XPathUtils.selectStringValueNormalize(currentElement, elementPath);
return configDirectory != null ? configDirectory : getPropertySet().getString(FileSerializer.DIRECTORY_PROPERTY);
}
public static void copyFile(File sourceFile, File destFile) {
try {
if(!destFile.exists()) {
destFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
}
finally {
if(source != null) {
source.close();
}
if(destination != null) {
destination.close();
}
}
}
catch (IOException e) {
throw new OXFException("Cannot copy file" + sourceFile + " to " + destFile, e);
}
}
private void setupScp(Scp scp, Element currentElement) {
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@file");
if (value != null)
scp.setFile(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@todir");
if (value != null)
scp.setTodir(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@localFile");
if (value != null)
scp.setLocalFile(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@remoteFile");
if (value != null)
scp.setRemoteFile(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@localTodir");
if (value != null)
scp.setLocalTodir(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@remoteTodir");
if (value != null)
scp.setRemoteTodir(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@localTofile");
if (value != null)
scp.setLocalTofile(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@remoteTofile");
if (value != null)
scp.setRemoteTofile(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@failonerror");
if (value != null)
scp.setFailonerror(Boolean.parseBoolean(value));
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@verbose");
if (value != null)
scp.setVerbose(Boolean.parseBoolean(value));
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@password");
if (value != null)
scp.setPassword(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@keyfile");
if (value != null)
scp.setKeyfile(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@passphrase");
if (value != null)
scp.setPassphrase(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@knownhost");
if (value != null)
scp.setKnownhosts(value);
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@trust");
if (value != null)
scp.setTrust(Boolean.parseBoolean(value));
}
{
final String value = XPathUtils.selectStringValueNormalize(currentElement, "@port");
if (value != null)
scp.setPort(Integer.parseInt(value));
}
}
}