Commit 051f80f3 authored by Alex Wenckus's avatar Alex Wenckus Committed by alex

Work on file transfer framework.

1) Added several interfaces to allow for easier extension and modification
2) Tracking of file transfer meta parameters
3) Default implementation forces proxy transfer to be associated with a file transfer negotiation that occured in the system

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3762 b35dd754-fafc-0310-a699-88a17e54d16e
parent 49db6756
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.dom4j.Element;
import java.util.Map;
import java.util.List;
/**
* Provides several utility methods for file transfer manager implementaions to utilize.
*
* @author Alexander Wenckus
*/
public abstract class AbstractFileTransferManager implements FileTransferManager {
private static final String CACHE_NAME = "File Transfer Cache";
private final Map<String, FileTransfer> fileTransferMap;
/**
* True if a proxy transfer is required to have a matching file transfer.
*/
private boolean isMatchProxyTransfer;
/**
* Default constructor creates the cache.
*/
public AbstractFileTransferManager() {
fileTransferMap = CacheManager.initializeCache(CACHE_NAME, "fileTransfer", 128 * 1024, 1000 * 60 * 10);
isMatchProxyTransfer = JiveGlobals.getBooleanProperty("xmpp.proxy.transfer.required", true);
}
/**
* Returns true if the proxy transfer should be matched to an existing file transfer in the system.
*
* @return Returns true if the proxy transfer should be matched to an existing file transfer in the system.
*/
public boolean isMatchProxyTransfer() {
return isMatchProxyTransfer;
}
public void cacheFileTransfer(String key, FileTransfer transfer) {
fileTransferMap.put(key, transfer);
}
public FileTransfer retrieveFileTransfer(String key) {
return fileTransferMap.get(key);
}
public static Element getChildElement(Element element, String namespace) {
List elements = element.elements();
if (elements.isEmpty()) {
return null;
}
for (int i = 0; i < elements.size(); i++) {
Element childElement = (Element) elements.get(i);
String childNamespace = childElement.getNamespaceURI();
if (namespace.equals(childNamespace)) {
return childElement;
}
}
return null;
}
public void registerProxyTransfer(String transferDigest, ProxyTransfer proxyTransfer)
throws UnauthorizedException
{
FileTransfer transfer = retrieveFileTransfer(transferDigest);
if (isMatchProxyTransfer() && transfer == null) {
throw new UnauthorizedException("Unable to match proxy transfer with a file transfer");
}
else if (transfer == null) {
return;
}
transfer.setProgress(proxyTransfer);
cacheFileTransfer(transferDigest, transfer);
}
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
/**
* Contains all of the meta information associated with a file transfer.
*
* @author Alexander Wenckus
*/
public class FileTransfer {
private String sessionID;
private String initiator;
private String target;
private String fileName;
private long fileSize;
private String mimeType;
private FileTransferProgress progress;
public FileTransfer(String initiator, String target, String sessionID, String fileName,
long fileSize, String mimeType)
{
this.initiator = initiator;
this.target = target;
this.sessionID = sessionID;
this.fileName = fileName;
this.fileSize = fileSize;
this.mimeType = mimeType;
}
public String getSessionID() {
return sessionID;
}
public void setSessionID(String sessionID) {
this.sessionID = sessionID;
}
public String getInitiator() {
return initiator;
}
public void setInitiator(String initiator) {
this.initiator = initiator;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public FileTransferProgress getProgress() {
return progress;
}
public void setProgress(FileTransferProgress progress) {
this.progress = progress;
}
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
import org.xmpp.packet.JID;
import org.dom4j.Element;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
/**
* Manages all file transfer currently happening originating from and/or ending at users of the server. From here,
* file transfers can be administered and stats can be tracked.
*
* @author Alexander Wenckus
*/
public interface FileTransferManager {
/**
* Checks an incoming file transfer request to see if it should be accepted or rejected.
* If it is accepted true will be returned and if it is rejected false will be returned.
*
* @param packetID The packet ID of the packet being parsed.
* @param from The offerer The offerer of the file transfer.
* @param to The receiver The potential reciever of the file transfer.
* @param siElement The Stream Initiation element
* @return True if it should be accepted false if it should not.
*/
boolean acceptIncomingFileTransferRequest(String packetID, JID from, JID to, Element siElement);
/**
* Registers that a transfer has begun through the proxy connected to the server.
*
* @param transferDigest The digest of the initiator + target + sessionID that uniquely identifies a file transfer
* @param proxyTransfer The related proxy transfer.
* @throws UnauthorizedException Thrown when in the current server configuration this transfer should not be
* permitted.
*/
public void registerProxyTransfer(String transferDigest, ProxyTransfer proxyTransfer) throws UnauthorizedException;
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
/**
* An interface to track the progress of a file transfer through the server. This interface is used
* by {@link FileTransfer} to make this information available if it is in the system.
*/
public interface FileTransferProgress {
public long getAmountTransfered() throws UnsupportedOperationException;
public String getInitiator();
public String getTarget();
public String getSessionID();
}
/**
* $Revision $
* $Date $
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
......@@ -15,12 +16,13 @@ import org.dom4j.Element;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.filetransfer.spi.DefaultFileTransferManager;
import org.jivesoftware.wildfire.interceptor.InterceptorManager;
import org.jivesoftware.wildfire.interceptor.PacketInterceptor;
import org.jivesoftware.wildfire.interceptor.PacketRejectedException;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.disco.DiscoInfoProvider;
import org.jivesoftware.wildfire.disco.DiscoItemsProvider;
import org.jivesoftware.wildfire.disco.DiscoServerItem;
import org.jivesoftware.wildfire.disco.ServerItemsProvider;
import org.jivesoftware.wildfire.disco.*;
import org.jivesoftware.wildfire.forms.spi.XDataFormImpl;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
......@@ -54,12 +56,14 @@ public class FileTransferProxy extends BasicModule
private PacketRouter router;
private String proxyIP;
private ProxyConnectionManager connectionManager;
private FileTransferManager transferManager;
public FileTransferProxy() {
super("SOCKS5 file transfer proxy");
info = new IQHandlerInfo("query", NAMESPACE);
InterceptorManager.getInstance().addInterceptor(new FileTransferInterceptor());
}
public boolean handleIQ(IQ packet) throws UnauthorizedException {
......@@ -147,8 +151,12 @@ public class FileTransferProxy extends BasicModule
catch (UnknownHostException e) {
Log.error("Couldn't discover local host", e);
}
transferManager = getFileTransferManager();
connectionManager = new ProxyConnectionManager(transferManager);
}
connectionManager = new ProxyConnectionManager();
private FileTransferManager getFileTransferManager() {
return new DefaultFileTransferManager();
}
public void start() {
......@@ -157,8 +165,9 @@ public class FileTransferProxy extends BasicModule
if (isEnabled()) {
connectionManager.processConnections(getProxyPort());
routingTable.addRoute(getAddress(), this);
XMPPServer.getInstance().getIQDiscoItemsHandler()
.addComponentItem(getAddress().toString(), "Socks 5 Bytestreams Proxy");
XMPPServer server = XMPPServer.getInstance();
server.getIQDiscoItemsHandler().addComponentItem(getAddress().toString(), "Socks 5 Bytestreams Proxy");
}
else {
XMPPServer.getInstance().getIQDiscoItemsHandler()
......@@ -295,4 +304,30 @@ public class FileTransferProxy extends BasicModule
}
}
}
/**
* Interceptor to grab and validate file transfer meta information.
*/
private class FileTransferInterceptor implements PacketInterceptor {
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed)
throws PacketRejectedException {
// We only want packets recieved by the server
if (!processed && incoming && packet instanceof IQ) {
IQ iq = (IQ) packet;
Element childElement = iq.getChildElement();
String namespace = childElement.getNamespaceURI();
if ("http://jabber.org/protocol/si".equals(namespace)) {
// If this is a set, check the feature offer
if (iq.getType().equals(IQ.Type.set)) {
JID from = iq.getFrom();
JID to = iq.getTo();
String packetID = iq.getID();
if (!transferManager.acceptIncomingFileTransferRequest(packetID, from, to, childElement)) {
throw new PacketRejectedException();
}
}
}
}
}
}
}
/**
* $Revision$
* $Date$
* Copyright (C) 1999-2005 Jive Software. All rights reserved.
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
......@@ -10,6 +12,10 @@ package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheManager;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.filetransfer.spi.DefaultProxyTransfer;
import org.xmpp.packet.JID;
import java.io.*;
......@@ -39,12 +45,24 @@ public class ProxyConnectionManager {
private ExecutorService executor = Executors.newCachedThreadPool();
private Future<?> socketProcess;
private int proxyPort;
public ProxyConnectionManager() {
private FileTransferManager transferManager;
private String className;
static long amountTransfered = 0;
public ProxyConnectionManager(FileTransferManager manager) {
String cacheName = "File Transfer";
CacheManager.initializeCache(cacheName, "filetransfer", -1, 1000 * 60 * 10);
connectionMap = CacheManager.getCache(cacheName);
className = JiveGlobals.getProperty("provider.transfer.proxy",
"org.jivesoftware.wildfire.filetransfer.spi.DefaultProxyTransfer");
transferManager = manager;
}
/*
......@@ -64,7 +82,7 @@ public class ProxyConnectionManager {
socketProcess = executor.submit(new Runnable() {
public void run() {
ServerSocket serverSocket = null;
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(port);
}
......@@ -72,7 +90,7 @@ public class ProxyConnectionManager {
Log.error("Error creating server socket", e);
return;
}
while (serverSocket != null) {
while (serverSocket.isBound()) {
final Socket socket;
try {
socket = serverSocket.accept();
......@@ -89,6 +107,12 @@ public class ProxyConnectionManager {
catch (IOException ie) {
Log.error("Error processing file transfer proxy connection",
ie);
try {
socket.close();
}
catch (IOException e) {
/* Do Nothing */
}
}
}
});
......@@ -140,42 +164,69 @@ public class ProxyConnectionManager {
out.write(cmd);
String responseDigest = processIncomingSocks5Message(in);
cmd = createOutgoingSocks5Message(0, responseDigest);
synchronized (connectionLock) {
ProxyTransfer transfer = connectionMap.get(responseDigest);
if (transfer == null) {
connectionMap.put(responseDigest, new ProxyTransfer(responseDigest, connection));
}
else {
transfer.setInitiatorSocket(connection);
try {
synchronized (connectionLock) {
ProxyTransfer transfer = connectionMap.get(responseDigest);
if (transfer == null) {
transfer = createProxyTransfer(responseDigest, connection);
transferManager.registerProxyTransfer(responseDigest, transfer);
connectionMap.put(responseDigest, transfer);
}
else {
transfer.setInitiatorSocket(connection);
}
}
cmd = createOutgoingSocks5Message(0, responseDigest);
out.write(cmd);
}
catch (UnauthorizedException eu) {
cmd = createOutgoingSocks5Message(2, responseDigest);
out.write(cmd);
throw new IOException("Illegal proxy transfer");
}
}
if (!connection.isConnected()) {
throw new IOException("Socket closed by remote user");
private ProxyTransfer createProxyTransfer(String transferDigest, Socket initiatorSocket) {
ProxyTransfer provider;
try {
Class c = ClassUtils.forName(className);
provider = (ProxyTransfer) c.newInstance();
}
out.write(cmd);
catch (Exception e) {
Log.error("Error loading proxy transfer provider: " + className, e);
provider = new DefaultProxyTransfer();
}
provider.setTransferDigest(transferDigest);
provider.setTargetSocket(initiatorSocket);
return provider;
}
private String processIncomingSocks5Message(InputStream in)
throws IOException {
private static String processIncomingSocks5Message(InputStream in)
throws IOException
{
// read the version and command
byte[] cmd = new byte[5];
in.read(cmd, 0, 5);
int read = in.read(cmd, 0, 5);
if (read != 5) {
throw new IOException("Error reading Socks5 version and command");
}
// read the digest
byte[] addr = new byte[cmd[4]];
in.read(addr, 0, addr.length);
read = in.read(addr, 0, addr.length);
if (read != addr.length) {
throw new IOException("Error reading provided address");
}
String digest = new String(addr);
in.read();
in.read();
read = in.read();
read = in.read();
return digest;
}
private byte[] createOutgoingSocks5Message(int cmd, String digest) {
private static byte[] createOutgoingSocks5Message(int cmd, String digest) {
byte addr[] = digest.getBytes();
byte[] data = new byte[7 + addr.length];
......@@ -202,9 +253,9 @@ public class ProxyConnectionManager {
* packet after both parties have connected to the proxy.
*
* @param initiator The initiator or sender of the file transfer.
* @param target The target or reciever of the file transfer.
* @param sid The sessionid the uniquely identifies the transfer between
* the two participants.
* @param target The target or reciever of the file transfer.
* @param sid The sessionid the uniquely identifies the transfer between
* the two participants.
* @throws IllegalArgumentException This exception is thrown when the activated transfer does
* not exist or is missing one or both of the realted sockets.
*/
......@@ -222,13 +273,13 @@ public class ProxyConnectionManager {
throw new IllegalArgumentException("Transfer doesn't exist or is missing parameters");
}
transfer.setInitiatorJID(initiator.toString());
transfer.setTargetJID(target.toString());
transfer.setTransferSession(sid);
transfer.setInitiator(initiator.toString());
transfer.setTarget(target.toString());
transfer.setSessionID(sid);
transfer.setTransferFuture(executor.submit(new Runnable() {
public void run() {
try {
transfer(transfer);
transfer.doTransfer();
}
catch (IOException e) {
Log.error("Error during file transfer", e);
......@@ -240,40 +291,18 @@ public class ProxyConnectionManager {
}));
}
private void transfer(ProxyTransfer transfer) throws IOException {
InputStream in = transfer.getInitiatorSocket().getInputStream();
OutputStream out = transfer.getTargetSocket().getOutputStream();
final byte[] b = new byte[1000];
int count = 0;
int amountWritten = 0;
count = in.read(b);
while (count != -1) {
// write to the output stream
out.write(b, 0, count);
amountWritten += count;
// read more bytes from the input stream
count = in.read(b);
}
transfer.getInitiatorSocket().close();
transfer.getTargetSocket().close();
}
/**
* Creates the digest needed for a byte stream. It is the SHA1(sessionID +
* initiator + target).
*
* @param sessionID The sessionID of the stream negotiation
* @param initiator The inititator of the stream negotiation
* @param target The target of the stream negotiation
* @param target The target of the stream negotiation
* @return SHA-1 hash of the three parameters
*/
private String createDigest(final String sessionID, final JID initiator,
final JID target) {
public static String createDigest(final String sessionID, final JID initiator,
final JID target)
{
return hash(sessionID + initiator.getNode()
+ "@" + initiator.getDomain() + "/"
+ initiator.getResource()
......
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2005 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Cacheable;
import java.net.Socket;
import java.util.concurrent.Future;
/**
* Tracks the different connections related to a file transfer. There are two connections, the
* initiator and the target and when both connections are completed the transfer can begin.
*/
public class ProxyTransfer implements Cacheable {
private String initiatorJID;
private Socket initiatorSocket;
private Socket targetSocket;
private String targetJID;
private String transferDigest;
private String transferSession;
private Future<?> future;
public ProxyTransfer(String transferDigest, Socket targetSocket) {
this.transferDigest = transferDigest;
this.targetSocket = targetSocket;
}
public String getInitiatorJID() {
return initiatorJID;
}
public void setInitiatorJID(String initiatorJID) {
this.initiatorJID = initiatorJID;
}
public Socket getInitiatorSocket() {
return initiatorSocket;
}
public void setInitiatorSocket(Socket initiatorSocket) {
this.initiatorSocket = initiatorSocket;
}
public Socket getTargetSocket() {
return targetSocket;
}
public void setTargetSocket(Socket targetSocket) {
this.targetSocket = targetSocket;
}
public String getTargetJID() {
return targetJID;
}
public void setTargetJID(String targetJID) {
this.targetJID = targetJID;
}
public String getTransferDigest() {
return transferDigest;
}
public void setTransferDigest(String transferDigest) {
this.transferDigest = transferDigest;
}
public String getTransferSession() {
return transferSession;
}
public void setTransferSession(String transferSession) {
this.transferSession = transferSession;
}
/**
* Returns true if the Bytestream is ready to be activated and the transfer can begin.
*
* @return Returns true if the Bytestream is ready to be activated.
*/
public boolean isActivatable() {
return ((initiatorSocket != null) && (targetSocket != null));
}
public void setTransferFuture(Future<?> future) {
this.future = future;
}
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfString(initiatorJID);
size += CacheSizes.sizeOfString(targetJID);
size += CacheSizes.sizeOfString(transferDigest);
size += CacheSizes.sizeOfString(transferSession);
return size;
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2005 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.Cacheable;
import java.net.Socket;
import java.io.IOException;
import java.util.concurrent.Future;
/**
* Tracks the different connections related to a proxy file transfer. There are two connections, the
* initiator and the target and when both connections are completed the transfer can begin.
*/
public interface ProxyTransfer extends Cacheable, FileTransferProgress {
/**
* Returns the fully qualified JID of the initiator of the file transfer.
*
* @return Returns the fully qualified JID of the initiator of the file transfer.
*/
public String getInitiator();
public void setInitiatorSocket(Socket initiatorSocket);
public Socket getInitiatorSocket();
public void setTargetSocket(Socket targetSocket);
public Socket getTargetSocket();
public void setTarget(String target);
public String getTarget();
public void setTransferDigest(String digest);
public String getTransferDigest();
public String getSessionID();
public void setSessionID(String streamID);
/**
* Returns true if the Bytestream is ready to be activated and the transfer can begin.
*
* @return Returns true if the Bytestream is ready to be activated.
*/
public boolean isActivatable();
public long getAmountTransfered();
public int getCachedSize();
public void setTransferFuture(Future<?> future);
/**
* Transfers the file from the initiator to the target.
*/
public void doTransfer() throws IOException;
void setInitiator(String s);
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer.spi;
import org.xmpp.packet.JID;
import org.dom4j.Element;
import org.jivesoftware.wildfire.filetransfer.AbstractFileTransferManager;
import org.jivesoftware.wildfire.filetransfer.FileTransfer;
import org.jivesoftware.wildfire.filetransfer.ProxyConnectionManager;
/**
*
*/
public class DefaultFileTransferManager extends AbstractFileTransferManager {
public boolean acceptIncomingFileTransferRequest(String packetID, JID from, JID to, Element siElement) {
String streamID = siElement.attributeValue("id");
String mimeType = siElement.attributeValue("mime-type");
String profile = siElement.attributeValue("profile");
// Check profile, the only type we deal with currently is file transfer
if ("http://jabber.org/protocol/si/profile/file-transfer".equals(profile)) {
Element fileTransferElement =
getChildElement(siElement, "http://jabber.org/protocol/si/profile/file-transfer");
// Not valid form, reject
if (fileTransferElement == null) {
return false;
}
String fileName = fileTransferElement.attributeValue("name");
long size = Long.parseLong(fileTransferElement.attributeValue("size"));
FileTransfer transfer = new FileTransfer(from.toString(), to.toString(),
streamID, fileName, size, mimeType);
cacheFileTransfer(ProxyConnectionManager.createDigest(streamID, from, to), transfer);
}
return true;
}
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer.spi;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.wildfire.filetransfer.ProxyTransfer;
import java.net.Socket;
import java.util.concurrent.Future;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;
/**
* Tracks the different connections related to a file transfer. There are two connections, the
* initiator and the target and when both connections are completed the transfer can begin.
*/
public class DefaultProxyTransfer implements ProxyTransfer {
private static long amountTransfered;
private String initiator;
private Socket initiatorSocket;
private Socket targetSocket;
private String target;
private String transferDigest;
private String streamID;
private Future<?> future;
private long amountWritten;
public DefaultProxyTransfer() { }
public String getInitiator() {
return initiator;
}
/**
* Sets the fully qualified JID of the initiator of the file transfer.
*
* @param initiator The fully qualified JID of the initiator of the file transfer.
*/
public void setInitiator(String initiator) {
this.initiator = initiator;
}
public Socket getInitiatorSocket() {
return initiatorSocket;
}
public void setInitiatorSocket(Socket initiatorSocket) {
this.initiatorSocket = initiatorSocket;
}
public Socket getTargetSocket() {
return targetSocket;
}
public void setTargetSocket(Socket targetSocket) {
this.targetSocket = targetSocket;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getTransferDigest() {
return transferDigest;
}
public void setTransferDigest(String transferDigest) {
this.transferDigest = transferDigest;
}
public String getSessionID() {
return streamID;
}
public void setSessionID(String streamID) {
this.streamID = streamID;
}
public boolean isActivatable() {
return ((initiatorSocket != null) && (targetSocket != null));
}
public synchronized void setTransferFuture(Future<?> future) {
if(this.future != null) {
throw new IllegalStateException("Transfer is already in progress, or has completed.");
}
this.future = future;
}
public long getAmountTransfered() {
return amountWritten;
}
public void doTransfer() throws IOException {
if(!isActivatable()) {
throw new IOException("Transfer missing party");
}
InputStream in = getInitiatorSocket().getInputStream();
OutputStream out = new ProxyOutputStream(getTargetSocket().getOutputStream());
final byte[] b = new byte[1000];
int count = 0;
amountWritten = 0;
do {
// write to the output stream
out.write(b, 0, count);
amountWritten += count;
// read more bytes from the input stream
count = in.read(b);
} while (count >= 0);
getInitiatorSocket().close();
getTargetSocket().close();
}
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfString(initiator);
size += CacheSizes.sizeOfString(target);
size += CacheSizes.sizeOfString(transferDigest);
size += CacheSizes.sizeOfString(streamID);
size += CacheSizes.sizeOfLong(); // Amount written
size += CacheSizes.sizeOfObject(); // Initiatior Socket
size += CacheSizes.sizeOfObject(); // Target socket
size += CacheSizes.sizeOfObject(); // Future
return size;
}
static class ProxyOutputStream extends DataOutputStream {
public ProxyOutputStream(OutputStream out) {
super(out);
}
public synchronized void write(byte b[], int off, int len) throws IOException {
super.write(b, off, len);
amountTransfered += len;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment