/*************************************************************************** * Copyright 2006-2016 by Christian Ihle * * contact@kouchat.net * * * * This file is part of KouChat. * * * * KouChat 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 3 of * * the License, or (at your option) any later version. * * * * KouChat 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 KouChat. * * If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ package net.usikkert.kouchat.net; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.logging.Level; import java.util.logging.Logger; import net.usikkert.kouchat.event.FileTransferListener; import net.usikkert.kouchat.misc.User; import net.usikkert.kouchat.util.ByteCounter; /** * This is a class for sending files to other users. * * <p>To send a file, the user at the other end needs to * open a server socket so this client can connect.</p> * * @author Christian Ihle */ public class FileSender implements FileTransfer { /** The logger. */ private static final Logger LOG = Logger.getLogger(FileSender.class.getName()); /** The user to send a file to. */ private final User user; /** The file to send to the user. */ private final FileToSend file; /** The unique ID of this file transfer. */ private final int id; /** Keeps count of the transfer speed. */ private final ByteCounter bCounter; /** Percent of the file transferred. */ private int percent; /** Number of bytes transferred. */ private long transferred; /** If the file was successfully sent. */ private boolean sent; /** If the file transfer is canceled. */ private boolean cancel; /** If still waiting for the file transfer to begin. */ private boolean waiting; /** The file transfer listener. */ private FileTransferListener listener; /** The input stream from the file. */ private InputStream is; /** The output stream to the other user. */ private OutputStream os; /** The socket connection to the other user. */ private Socket sock; /** * Constructor. Creates a new file sender. * * @param user The user to send the file to. * @param file The file to send. * @param id The unique ID of this file transfer. */ public FileSender(final User user, final FileToSend file, final int id) { this.user = user; this.file = file; this.id = id; bCounter = new ByteCounter(); waiting = true; } /** * Connects to the user at the specified port and transfers the file * to that user. * * @param port The port to use when connecting to the user. * @return If the file transfer was successful. */ public boolean transfer(final int port) { if (!cancel) { listener.statusConnecting(); waiting = false; sent = false; try { int counter = 0; while (sock == null && counter < 10) { counter++; try { sock = new Socket(InetAddress.getByName(user.getIpAddress()), port); } catch (final UnknownHostException e) { LOG.log(Level.SEVERE, e.toString(), e); } catch (final IOException e) { LOG.log(Level.SEVERE, e.toString(), e); } try { Thread.sleep(100); } catch (final InterruptedException e) { LOG.log(Level.SEVERE, e.toString(), e); } } if (sock != null && !cancel) { listener.statusTransferring(); is = file.getInputStream(); os = sock.getOutputStream(); final byte[] b = new byte[1024]; transferred = 0; percent = 0; int tmpTransferred = 0; int tmpPercent = 0; int transCounter = 0; bCounter.prepare(); while (!cancel && (tmpTransferred = is.read(b)) != -1) { os.write(b, 0, tmpTransferred); transferred += tmpTransferred; percent = (int) ((transferred * 100) / file.length()); bCounter.addBytes(tmpTransferred); transCounter++; if (percent > tmpPercent || transCounter >= 250) { transCounter = 0; tmpPercent = percent; listener.transferUpdate(); } } if (!cancel && transferred == file.length()) { sent = true; listener.statusCompleted(); } else { listener.statusFailed(); } } else { listener.statusFailed(); } } catch (final UnknownHostException e) { LOG.log(Level.SEVERE, e.toString(), e); listener.statusFailed(); } catch (final IOException e) { LOG.log(Level.SEVERE, e.toString()); listener.statusFailed(); } finally { stopSender(); cleanupConnections(); } } return sent; } /** * Sets all connections to null. */ private void cleanupConnections() { is = null; os = null; sock = null; } /** * Closes the connection to the user. */ private void stopSender() { try { if (is != null) { is.close(); } } catch (final IOException e) { LOG.log(Level.SEVERE, e.toString(), e); } try { if (os != null) { os.flush(); } } catch (final IOException e) { LOG.log(Level.SEVERE, e.toString(), e); } try { if (os != null) { os.close(); } } catch (final IOException e) { LOG.log(Level.SEVERE, e.toString(), e); } try { if (sock != null) { sock.close(); } } catch (final IOException e) { LOG.log(Level.SEVERE, e.toString(), e); } } /** * Checks if the file transfer has been canceled. * * @return If the file transfer has been canceled. */ @Override public boolean isCanceled() { return cancel; } /** * Cancels the file transfer. */ @Override public void cancel() { cancel = true; stopSender(); listener.statusFailed(); } /** * Checks if the file transfer is complete. * * @return If the file transfer is complete. */ @Override public boolean isTransferred() { return sent; } /** * The percent of the file transfer that is completed. * * @return Percent completed. */ @Override public int getPercent() { return percent; } /** * The other user, which receives a file. * * @return The other user. */ @Override public User getUser() { return user; } /** * Number of bytes transferred. * * @return Bytes transferred. */ @Override public long getTransferred() { return transferred; } /** * Gets the name of the file being transferred. * * @return The name of the file. */ @Override public String getFileName() { return file.getName(); } /** * Gets the size of the file being transferred, in bytes. * * @return The file size. */ @Override public long getFileSize() { return file.length(); } /** * Gets the direction, which is send. * * @return Send, the direction of the file transfer. */ @Override public Direction getDirection() { return Direction.SEND; } /** * Gets the number of bytes transferred per second. * * @return The speed in bytes per second. */ @Override public long getSpeed() { return bCounter.getBytesPerSec(); } /** * Gets the ID of this file transfer. The ID is unique during the session, and starts with 1. * * @return The unique ID of this file transfer. */ @Override public int getId() { return id; } /** * Gets the file that is being transferred. * * @return The file. */ public FileToSend getFile() { return file; } /** * If still waiting for the file transfer to begin. * * @return If waiting or not. */ public boolean isWaiting() { return waiting; } /** * Registers a file transfer listener, which will receive updates * when certain events happen in the progression of the file transfer. * * @param listener The listener to register. */ @Override public void registerListener(final FileTransferListener listener) { this.listener = listener; listener.statusWaiting(); } }