/*
* Copyright (C) 2004 Anthony Smith
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* ----------------------------------------------------------------------------
* TITLE $Id$
* ---------------------------------------------------------------------------
*
* --------------------------------------------------------------------------*/
package opendbcopy.plugin.jsch.module;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import opendbcopy.config.XMLTags;
import opendbcopy.plugin.jsch.config.JschXMLTags;
import opendbcopy.plugin.model.exception.MissingAttributeException;
import opendbcopy.plugin.model.exception.MissingElementException;
import opendbcopy.plugin.model.exception.PluginException;
import opendbcopy.util.InputOutputHelper;
import org.apache.log4j.Logger;
import org.jdom.Element;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* class description
*
* @author Anthony Smith
* @version $Revision$
*/
public final class ScpTo {
/**
* DOCUMENT ME!
*
* @param logger DOCUMENT ME!
* @param conf DOCUMENT ME!
* @param input DOCUMENT ME!
* @param output DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
public static final void execute(Logger logger,
Element conf,
Element input,
Element output) throws PluginException {
File[] inputFiles = null;
URL[] outputURLs = null;
ArrayList remoteURLs = new ArrayList();
try {
inputFiles = retrieveInput(conf, input);
String remotePath = retrieveOutputPath(conf);
String host = retrieveHost(conf);
int port = retrievePort(conf);
String userName = retrieveUserName(conf);
String password = retrievePassword(conf);
String fileListIdentifier = retrieveFileListIdentifier(conf);
if ((inputFiles != null) && (inputFiles.length > 0)) {
for (int i = 0; i < inputFiles.length; i++) {
File inputFile = inputFiles[i];
remoteURLs.add(new URL("file", host, -1, remotePath + "/" + inputFile.getName()));
secureCopyDir(logger, host, port, userName, password, inputFile, remotePath);
}
}
outputURLs = new URL[remoteURLs.size()];
outputURLs = (URL[]) remoteURLs.toArray(outputURLs);
Element outputElement = InputOutputHelper.createURLListElement(outputURLs);
output.addContent(outputElement);
} catch (MissingAttributeException e) {
throw new PluginException(e);
} catch (MissingElementException e) {
throw new PluginException(e);
} catch (FileNotFoundException e) {
throw new PluginException(e);
} catch (IOException e) {
throw new PluginException(e);
} catch (Exception e) {
throw new PluginException(e);
}
}
/**
* Only works if the remote directory already exists or is created first, which it isn't at the moment
*
* @param logger DOCUMENT ME!
* @param host DOCUMENT ME!
* @param port DOCUMENT ME!
* @param userName DOCUMENT ME!
* @param password DOCUMENT ME!
* @param sourceFile DOCUMENT ME!
* @param remotePath DOCUMENT ME!
*
* @throws IOException DOCUMENT ME!
* @throws JSchException DOCUMENT ME!
* @throws PluginException DOCUMENT ME!
*/
private static void secureCopyDir(Logger logger,
String host,
int port,
String userName,
String password,
File sourceFile,
String remotePath) throws IOException, JSchException, PluginException {
if (sourceFile.isDirectory()) {
File[] fileList = sourceFile.listFiles();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isDirectory()) {
secureCopyDir(logger, host, port, userName, password, fileList[i], remotePath + "/" + sourceFile.getName() + "/" + fileList[i].getName());
} else if (fileList[i].isFile()) {
secureCopyFile(logger, host, port, userName, password, fileList[i], remotePath + "/" + sourceFile.getName() + "/" + fileList[i].getName());
}
}
} else {
secureCopyFile(logger, host, port, userName, password, sourceFile, remotePath + "/" + sourceFile.getName());
}
}
/**
* DOCUMENT ME!
*
* @param logger DOCUMENT ME!
* @param host DOCUMENT ME!
* @param port DOCUMENT ME!
* @param userName DOCUMENT ME!
* @param password DOCUMENT ME!
* @param sourceFile DOCUMENT ME!
* @param remotePath DOCUMENT ME!
*
* @throws IOException DOCUMENT ME!
* @throws JSchException DOCUMENT ME!
* @throws PluginException DOCUMENT ME!
*/
private static void secureCopyFile(Logger logger,
String host,
int port,
String userName,
String password,
File sourceFile,
String remotePath) throws IOException, JSchException, PluginException {
JSch jsch = new JSch();
Session session = jsch.getSession(userName, host, port);
session.setHost(host);
session.setUserName(userName);
session.setPassword(password);
session.connect();
// exec 'scp -t rfile' remotely
String command = "scp -t " + remotePath;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
// get I/O streams for remote scp
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[1];
if (checkAck(in) != 0) {
throw new PluginException("Acknowledgment error");
}
// send "C0644 filesize filename", where filename should not include '/'
int filesize = (int) (sourceFile).length();
command = "C0644 " + filesize + " ";
command += sourceFile.getName();
command += "\n";
out.write(command.getBytes());
out.flush();
if (checkAck(in) != 0) {
throw new PluginException("Acknowledgment error");
}
// send a content of sourceFile
FileInputStream fis = new FileInputStream(sourceFile);
byte[] buf = new byte[1024];
while (true) {
int len = fis.read(buf, 0, buf.length);
if (len <= 0) {
break;
}
out.write(buf, 0, len);
out.flush();
}
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
if (checkAck(in) != 0) {
throw new PluginException("Acknowledgment error");
}
logger.info("copied local file " + sourceFile.getAbsolutePath() + " to " + host + remotePath);
}
/**
* DOCUMENT ME!
*
* @param in DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IOException DOCUMENT ME!
* @throws PluginException DOCUMENT ME!
*/
private static int checkAck(InputStream in) throws IOException, PluginException {
int b = in.read();
// b may be 0 for success,
// 1 for error,
// 2 for fatal error,
// -1
if (b == 0) {
return b;
}
if (b == -1) {
return b;
}
if ((b == 1) || (b == 2)) {
StringBuffer sb = new StringBuffer();
int c;
do {
c = in.read();
sb.append((char) c);
} while (c != '\n');
if (b == 1) { // error
throw new PluginException("Acknowledgment error: " + sb.toString());
}
if (b == 2) { // fatal error
throw new PluginException("Acknowledgment fatal error: " + sb.toString());
}
}
return b;
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
* @param input DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws MissingAttributeException DOCUMENT ME!
* @throws MissingElementException DOCUMENT ME!
* @throws FileNotFoundException DOCUMENT ME!
* @throws PluginException DOCUMENT ME!
*/
private static File[] retrieveInput(Element conf,
Element input) throws MissingAttributeException, MissingElementException, FileNotFoundException, PluginException {
ArrayList inputFiles = new ArrayList();
Element inputConf = conf.getChild(XMLTags.INPUT);
if (inputConf == null) {
throw new PluginException(new MissingElementException(new Element(XMLTags.INPUT), XMLTags.INPUT));
}
// check what shall be taken as input
String inputConfSelection = inputConf.getChild(XMLTags.FILE_DIR_FILELISTS_SELECTION).getAttributeValue(XMLTags.VALUE);
if ((inputConfSelection != null) && (inputConfSelection.length() > 0)) {
if (inputConfSelection.compareToIgnoreCase(XMLTags.FILE) == 0) {
inputConfSelection = inputConf.getChild(XMLTags.FILE).getAttributeValue(XMLTags.VALUE);
if ((inputConfSelection == null) || (inputConfSelection.length() == 0)) {
throw new PluginException("Missing file specified in input configuration");
}
// try to retrieve file and check if it exists
File inputFile = new File(inputConfSelection);
if (!inputFile.exists()) {
throw new PluginException("File " + inputFile.getAbsolutePath() + " does not exist!");
} else {
inputFiles.add(inputFile);
}
} else if (inputConfSelection.compareTo(XMLTags.DIR) == 0) {
inputConfSelection = inputConf.getChild(XMLTags.DIR).getAttributeValue(XMLTags.VALUE);
if ((inputConfSelection == null) || (inputConfSelection.length() == 0)) {
throw new PluginException("Missing directory specified in input configuration");
}
// try to retrieve file and check if it exists
File inputFile = new File(inputConfSelection);
if (!inputFile.exists()) {
throw new PluginException("Directory " + inputFile.getAbsolutePath() + " does not exist!");
} else {
inputFiles.add(inputFile);
}
}
// input is provided by filelists from former plugin in its output
else if (inputConfSelection.compareTo(XMLTags.FILELISTS) == 0) {
if (input == null) {
throw new PluginException("Missing output from former plugin!");
}
if (input.getChildren(XMLTags.FILELIST).size() == 0) {
throw new PluginException("Missing output filelists from former plugin!");
}
List inputFileLists = input.getChildren(XMLTags.FILELIST);
if ((inputFileLists != null) && (inputFileLists.size() > 0)) {
Iterator itInputFileLists = inputFileLists.iterator();
while (itInputFileLists.hasNext()) {
Element fileList = (Element) itInputFileLists.next();
inputFiles.addAll(InputOutputHelper.getFileList(fileList));
}
}
} else {
throw new PluginException("You must specify an input type");
}
}
if (inputFiles != null) {
File[] inputFilesArray = new File[inputFiles.size()];
return (File[]) inputFiles.toArray(inputFilesArray);
} else {
return null;
}
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
private static String retrieveOutputPath(Element conf) throws PluginException {
if ((conf.getChild(JschXMLTags.REMOTE_DIR) != null) && (conf.getChild(JschXMLTags.REMOTE_DIR).getAttributeValue(XMLTags.VALUE).length() > 0)) {
return conf.getChild(JschXMLTags.REMOTE_DIR).getAttributeValue(XMLTags.VALUE);
} else {
throw new PluginException("Missing output path for remote directory");
}
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
private static String retrieveHost(Element conf) throws PluginException {
if ((conf.getChild(JschXMLTags.HOST) != null) && (conf.getChild(JschXMLTags.HOST).getAttributeValue(XMLTags.VALUE).length() > 0)) {
return conf.getChild(JschXMLTags.HOST).getAttributeValue(XMLTags.VALUE);
} else {
throw new PluginException("Missing Host Name");
}
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
private static int retrievePort(Element conf) throws PluginException {
if ((conf.getChild(JschXMLTags.PORT) != null) && (conf.getChild(JschXMLTags.PORT).getAttributeValue(XMLTags.VALUE).length() > 0)) {
return Integer.parseInt(conf.getChild(JschXMLTags.PORT).getAttributeValue(XMLTags.VALUE));
} else {
throw new PluginException("Missing Port Number");
}
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
private static String retrieveUserName(Element conf) throws PluginException {
if ((conf.getChild(XMLTags.USERNAME) != null) && (conf.getChild(XMLTags.USERNAME).getAttributeValue(XMLTags.VALUE).length() > 0)) {
return conf.getChild(XMLTags.USERNAME).getAttributeValue(XMLTags.VALUE);
} else {
throw new PluginException("Missing User Name");
}
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
private static String retrievePassword(Element conf) throws PluginException {
if (conf.getChild(XMLTags.PASSWORD) != null) {
return conf.getChild(XMLTags.PASSWORD).getAttributeValue(XMLTags.VALUE);
} else {
throw new PluginException("Missing Password");
}
}
/**
* DOCUMENT ME!
*
* @param conf DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PluginException DOCUMENT ME!
*/
private static String retrieveFileListIdentifier(Element conf) throws PluginException {
if ((conf.getChild(XMLTags.OUTPUT).getChild(XMLTags.FILELIST).getAttributeValue(XMLTags.VALUE) != null) && (conf.getChild(XMLTags.OUTPUT).getChild(XMLTags.FILELIST).getAttributeValue(XMLTags.VALUE).length() > 0)) {
return conf.getChild(XMLTags.OUTPUT).getChild(XMLTags.FILELIST).getAttributeValue(XMLTags.VALUE);
} else {
throw new PluginException("Missing FileList identifier");
}
}
}