All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer Maven / Gradle / Ivy

/**
 *
 * Copyright 2003-2006 Jive Software.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jivesoftware.smackx.filetransfer;

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.util.logging.Logger;

import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.IllegalStateChangeException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.util.CloseableUtil;

import org.jxmpp.jid.Jid;

/**
 * Handles the sending of a file to another user. File transfer's in jabber have
 * several steps and there are several methods in this class that handle these
 * steps differently.
 *
 * @author Alexander Wenckus
 *
 */
public class OutgoingFileTransfer extends FileTransfer {
    private static final Logger LOGGER = Logger.getLogger(OutgoingFileTransfer.class.getName());

    private static int RESPONSE_TIMEOUT = 60 * 1000;
    private NegotiationProgress callback;

    /**
     * Returns the time in milliseconds after which the file transfer
     * negotiation process will timeout if the other user has not responded.
     *
     * @return Returns the time in milliseconds after which the file transfer
     *         negotiation process will timeout if the remote user has not
     *         responded.
     */
    public static int getResponseTimeout() {
        return RESPONSE_TIMEOUT;
    }

    /**
     * Sets the time in milliseconds after which the file transfer negotiation
     * process will timeout if the other user has not responded.
     *
     * @param responseTimeout TODO javadoc me please
     *            The timeout time in milliseconds.
     */
    public static void setResponseTimeout(int responseTimeout) {
        RESPONSE_TIMEOUT = responseTimeout;
    }

    private OutputStream outputStream;

    private Jid initiator;

    private Thread transferThread;

    protected OutgoingFileTransfer(Jid initiator, Jid target,
            String streamID, FileTransferNegotiator transferNegotiator) {
        super(target, streamID, transferNegotiator);
        this.initiator = initiator;
    }

    protected void setOutputStream(OutputStream stream) {
        if (outputStream == null) {
            this.outputStream = stream;
        }
    }

    /**
     * Returns the output stream connected to the peer to transfer the file. It
     * is only available after it has been successfully negotiated by the
     * {@link StreamNegotiator}.
     *
     * @return Returns the output stream connected to the peer to transfer the
     *         file.
     */
    protected OutputStream getOutputStream() {
        if (getStatus().equals(FileTransfer.Status.negotiated)) {
            return outputStream;
        } else {
            return null;
        }
    }

    /**
     * This method handles the negotiation of the file transfer and the stream,
     * it only returns the created stream after the negotiation has been completed.
     *
     * @param fileName TODO javadoc me please
     *            The name of the file that will be transmitted. It is
     *            preferable for this name to have an extension as it will be
     *            used to determine the type of file it is.
     * @param fileSize TODO javadoc me please
     *            The size in bytes of the file that will be transmitted.
     * @param description TODO javadoc me please
     *            A description of the file that will be transmitted.
     * @return The OutputStream that is connected to the peer to transmit the
     *         file.
     * @throws XMPPException if an XMPP protocol error was received.
     *             Thrown if an error occurs during the file transfer
     *             negotiation process.
     * @throws SmackException if there was no response from the server.
     * @throws InterruptedException if the calling thread was interrupted.
     */
    public synchronized OutputStream sendFile(String fileName, long fileSize,
            String description) throws XMPPException, SmackException, InterruptedException {
        if (isDone() || outputStream != null) {
            throw new IllegalStateException(
                    "The negotiation process has already"
                            + " been attempted on this file transfer");
        }
        try {
            setFileInfo(fileName, fileSize);
            this.outputStream = negotiateStream(fileName, fileSize, description);
        } catch (XMPPErrorException e) {
            handleXMPPException(e);
            throw e;
        }
        return outputStream;
    }

    /**
     * This methods handles the transfer and stream negotiation process. It
     * returns immediately and its progress will be updated through the
     * {@link NegotiationProgress} callback.
     *
     * @param fileName TODO javadoc me please
     *            The name of the file that will be transmitted. It is
     *            preferable for this name to have an extension as it will be
     *            used to determine the type of file it is.
     * @param fileSize TODO javadoc me please
     *            The size in bytes of the file that will be transmitted.
     * @param description TODO javadoc me please
     *            A description of the file that will be transmitted.
     * @param progress TODO javadoc me please
     *            A callback to monitor the progress of the file transfer
     *            negotiation process and to retrieve the OutputStream when it
     *            is complete.
     */
    public synchronized void sendFile(final String fileName,
            final long fileSize, final String description,
            final NegotiationProgress progress) {
        if (progress == null) {
            throw new IllegalArgumentException("Callback progress cannot be null.");
        }
        checkTransferThread();
        if (isDone() || outputStream != null) {
            throw new IllegalStateException(
                    "The negotiation process has already"
                            + " been attempted for this file transfer");
        }
        setFileInfo(fileName, fileSize);
        this.callback = progress;
        transferThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OutgoingFileTransfer.this.outputStream = negotiateStream(
                            fileName, fileSize, description);
                    progress.outputStreamEstablished(OutgoingFileTransfer.this.outputStream);
                }
                catch (XMPPErrorException e) {
                    handleXMPPException(e);
                }
                catch (Exception e) {
                    setException(e);
                }
            }
        }, "File Transfer Negotiation " + streamID);
        transferThread.start();
    }

    private void checkTransferThread() {
        if ((transferThread != null && transferThread.isAlive()) || isDone()) {
            throw new IllegalStateException(
                    "File transfer in progress or has already completed.");
        }
    }

    /**
     * This method handles the stream negotiation process and transmits the file
     * to the remote user. It returns immediately and the progress of the file
     * transfer can be monitored through several methods:
     *
     * 
    *
  • {@link FileTransfer#getStatus()} *
  • {@link FileTransfer#getProgress()} *
  • {@link FileTransfer#isDone()} *
* * @param file the file to transfer to the remote entity. * @param description a description for the file to transfer. * @throws SmackException if Smack detected an exceptional situation. * If there is an error during the negotiation process or the * sending of the file. */ public synchronized void sendFile(final File file, final String description) throws SmackException { checkTransferThread(); if (file == null || !file.exists() || !file.canRead()) { throw new IllegalArgumentException("Could not read file"); } else { setFileInfo(file.getAbsolutePath(), file.getName(), file.length()); } transferThread = new Thread(new Runnable() { @Override public void run() { try { outputStream = negotiateStream(file.getName(), file .length(), description); } catch (XMPPErrorException e) { handleXMPPException(e); return; } catch (Exception e) { setException(e); } if (outputStream == null) { return; } if (!updateStatus(Status.negotiated, Status.in_progress)) { return; } InputStream inputStream = null; try { inputStream = new FileInputStream(file); writeToStream(inputStream, outputStream); } catch (FileNotFoundException e) { setStatus(FileTransfer.Status.error); setError(Error.bad_file); setException(e); } catch (IOException e) { setStatus(FileTransfer.Status.error); setException(e); } finally { CloseableUtil.maybeClose(inputStream, LOGGER); CloseableUtil.maybeClose(outputStream, LOGGER); } updateStatus(Status.in_progress, FileTransfer.Status.complete); } }, "File Transfer " + streamID); transferThread.start(); } /** * This method handles the stream negotiation process and transmits the file * to the remote user. It returns immediately and the progress of the file * transfer can be monitored through several methods: * *
    *
  • {@link FileTransfer#getStatus()} *
  • {@link FileTransfer#getProgress()} *
  • {@link FileTransfer#isDone()} *
* * @param in the stream to transfer to the remote entity. * @param fileName the name of the file that is transferred * @param fileSize the size of the file that is transferred * @param description a description for the file to transfer. */ public synchronized void sendStream(final InputStream in, final String fileName, final long fileSize, final String description) { checkTransferThread(); setFileInfo(fileName, fileSize); transferThread = new Thread(new Runnable() { @Override public void run() { // Create packet filter. try { outputStream = negotiateStream(fileName, fileSize, description); } catch (XMPPErrorException e) { handleXMPPException(e); return; } catch (Exception e) { setException(e); } if (outputStream == null) { return; } if (!updateStatus(Status.negotiated, Status.in_progress)) { return; } try { writeToStream(in, outputStream); } catch (IOException e) { setStatus(FileTransfer.Status.error); setException(e); } finally { CloseableUtil.maybeClose(in, LOGGER); CloseableUtil.maybeClose(outputStream, LOGGER); } updateStatus(Status.in_progress, FileTransfer.Status.complete); } }, "File Transfer " + streamID); transferThread.start(); } public void setCallback(NegotiationProgress negotiationProcess) { this.callback = negotiationProcess; } private void handleXMPPException(XMPPErrorException e) { StanzaError error = e.getStanzaError(); if (error != null) { switch (error.getCondition()) { case forbidden: setStatus(Status.refused); return; case bad_request: setStatus(Status.error); setError(Error.not_acceptable); break; default: setStatus(FileTransfer.Status.error); } } setException(e); } /** * Returns the amount of bytes that have been sent for the file transfer. Or * -1 if the file transfer has not started. *

* Note: This method is only useful when the {@link #sendFile(File, String)} * method is called, as it is the only method that actually transmits the * file. * * @return Returns the amount of bytes that have been sent for the file * transfer. Or -1 if the file transfer has not started. */ public long getBytesSent() { return amountWritten; } private OutputStream negotiateStream(String fileName, long fileSize, String description) throws SmackException, XMPPException, InterruptedException { // Negotiate the file transfer profile if (!updateStatus(Status.initial, Status.negotiating_transfer)) { throw new IllegalStateChangeException(); } StreamNegotiator streamNegotiator = negotiator.negotiateOutgoingTransfer( getPeer(), streamID, fileName, fileSize, description, RESPONSE_TIMEOUT); // Negotiate the stream if (!updateStatus(Status.negotiating_transfer, Status.negotiating_stream)) { throw new IllegalStateChangeException(); } outputStream = streamNegotiator.createOutgoingStream(streamID, initiator, getPeer()); if (!updateStatus(Status.negotiating_stream, Status.negotiated)) { throw new IllegalStateChangeException(); } return outputStream; } @Override public void cancel() { setStatus(Status.cancelled); } @Override protected boolean updateStatus(Status oldStatus, Status newStatus) { boolean isUpdated = super.updateStatus(oldStatus, newStatus); if (callback != null && isUpdated) { callback.statusUpdated(oldStatus, newStatus); } return isUpdated; } @Override protected void setStatus(Status status) { Status oldStatus = getStatus(); super.setStatus(status); if (callback != null) { callback.statusUpdated(oldStatus, status); } } @Override protected void setException(Exception exception) { super.setException(exception); if (callback != null) { callback.errorEstablishingStream(exception); } } /** * A callback class to retrieve the status of an outgoing transfer * negotiation process. * * @author Alexander Wenckus * */ public interface NegotiationProgress { /** * Called when the status changes. * * @param oldStatus the previous status of the file transfer. * @param newStatus the new status of the file transfer. */ void statusUpdated(Status oldStatus, Status newStatus); /** * Once the negotiation process is completed the output stream can be * retrieved. * * @param stream the established stream which can be used to transfer the file to the remote * entity */ void outputStreamEstablished(OutputStream stream); /** * Called when an exception occurs during the negotiation progress. * * @param e the exception that occurred. */ void errorEstablishingStream(Exception e); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy