org.calrissian.mango.jms.stream.AbstractJmsFileTransferSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.calrissian.mango.jms.stream.AbstractJmsFileTransferSupport.java

Source

/*
 * Copyright (C) 2013 The Calrissian Authors
 *
 * 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.calrissian.mango.jms.stream;

import org.calrissian.mango.jms.stream.domain.Piece;
import org.calrissian.mango.jms.stream.domain.Request;
import org.calrissian.mango.jms.stream.domain.Response;
import org.calrissian.mango.jms.stream.domain.ResponseStatusEnum;
import org.calrissian.mango.jms.stream.utils.DestinationRequestor;
import org.calrissian.mango.jms.stream.utils.DomainMessageUtils;
import org.calrissian.mango.jms.stream.utils.MessageQueueListener;
import org.calrissian.mango.uri.support.UriStreamOpener;
import org.calrissian.mango.uri.support.impl.BasicStreamOpener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.SessionCallback;
import org.springframework.util.Assert;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

@Deprecated
public abstract class AbstractJmsFileTransferSupport {

    protected JmsTemplate jmsTemplate;
    Logger logger = LoggerFactory.getLogger(getClass());
    private int pieceSize = 10240;
    private Destination streamRequestDestination;
    private String hashAlgorithm;

    private UriStreamOpener streamOpener;

    private Logger logService;

    public AbstractJmsFileTransferSupport() {

        this.streamOpener = new BasicStreamOpener();
    }

    public void setStreamOpener(UriStreamOpener streamOpener) {
        this.streamOpener = streamOpener;
    }

    protected String generateId() {
        return UUID.randomUUID().toString();
    }

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public int getPieceSize() {
        return pieceSize;
    }

    public void setPieceSize(int pieceSize) {
        this.pieceSize = pieceSize;
    }

    public Destination getStreamRequestDestination() {
        return streamRequestDestination;
    }

    public void setStreamRequestDestination(Destination streamRequestDestination) {
        this.streamRequestDestination = streamRequestDestination;
    }

    public InputStream receiveStream(final Request req) throws JmsFileTransferException {
        try {
            logger.info(
                    "Broadcasting request for [" + req.getDownloadUri() + "] and id[" + req.getRequestId() + "]");
            Message returnMessage = sendWithResponse(new MessageCreator() {

                @Override
                public Message createMessage(Session session) throws JMSException {
                    return DomainMessageUtils.toRequestMessage(session, req);
                }

            }, getStreamRequestDestination());

            // No one could fulfill the request
            if (returnMessage == null) {
                logger.info("No one can fullfil this request [" + req.getDownloadUri() + "]");
                return null;
            }

            // start the stream transfer at this destination
            final Destination receiveAckDestination = returnMessage.getJMSReplyTo();
            final String sendDataDestination = UUID.randomUUID().toString();

            logger.info("Receiver[" + req.getRequestId() + "]: File Transfer starting");

            return new JmsFileReceiverInputStream(this, sendDataDestination, receiveAckDestination);
        } catch (Exception e) {
            throw new JmsFileTransferException(e);
        }
    }

    @SuppressWarnings("unchecked")
    public void sendStream(Request req, final Destination replyTo) throws IOException {

        DigestInputStream is;
        Assert.notNull(req, "Request cannot be null");
        final URI downloadUrl;
        try {
            downloadUrl = new URI(req.getDownloadUri());
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }
        try {

            is = new DigestInputStream(new BufferedInputStream(streamOpener.openStream(downloadUrl)),
                    MessageDigest.getInstance(getHashAlgorithm()));

        } catch (NoSuchAlgorithmException e) {
            throw new JmsFileTransferException(e);
        } catch (Throwable e) {

            logger.info("Error occurred opening stream: " + e);
            return;
        }

        MessageQueueListener queueListener = null;
        try {

            @SuppressWarnings("rawtypes")
            Message returnMessage = (Message) jmsTemplate.execute(new SessionCallback() {

                @Override
                public Object doInJms(Session session) throws JMSException {
                    DestinationRequestor requestor = null;
                    try {
                        Message responseMessage = DomainMessageUtils.toResponseMessage(session,
                                new Response(ResponseStatusEnum.ACCEPT));

                        // Actual file transfer should be done on a queue.
                        // Topics will not work
                        Destination streamTransferDestination = factoryDestination(session,
                                UUID.randomUUID().toString());
                        requestor = new DestinationRequestor(session, replyTo, streamTransferDestination,
                                jmsTemplate.getReceiveTimeout());
                        Message returnMessage = requestor.request(responseMessage);
                        requestor.close();
                        return returnMessage;
                    } finally {
                        if (requestor != null)
                            requestor.close();
                    }
                }

            }, true);

            // timeout
            if (returnMessage == null)
                return;
            Response response = DomainMessageUtils.fromResponseMessage(returnMessage);

            // cancel transfer
            if (!ResponseStatusEnum.STARTSEND.equals(response.getStatus()))
                return;

            final Destination receiveAckDestination = returnMessage.getJMSDestination();
            final Destination sendDataDestination = returnMessage.getJMSReplyTo();

            queueListener = new MessageQueueListener(this, receiveAckDestination);

            logger.info("Sender[" + req.getRequestId() + "]: Starting send to: " + sendDataDestination);

            byte[] buffer = new byte[getPieceSize()];
            int read = is.read(buffer);
            long placeInFile = 0;
            while (read >= 0) {
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                stream.write(buffer, 0, read);
                stream.close();
                final byte[] pieceData = stream.toByteArray();
                final Piece piece = new Piece(placeInFile, pieceData, getHashAlgorithm());
                logger.info("Sender[" + req.getRequestId() + "]: Sending piece with position: "
                        + piece.getPosition() + " Size of piece: " + pieceData.length);
                jmsTemplate.send(sendDataDestination, new MessageCreator() {

                    @Override
                    public Message createMessage(Session session) throws JMSException {
                        return DomainMessageUtils.toPieceMessage(session, piece);
                    }

                });
                //                Message ret = jmsTemplate.receive(receiveAckDestination);
                Message ret = queueListener.getMessageInQueue();
                logger.info("Sender[" + req.getRequestId() + "]: Sent piece and got ack");

                // no one on the other end any longer, timeout
                if (ret == null)
                    return;

                Response res = DomainMessageUtils.fromResponseMessage(ret);
                // stop transfer
                if (ResponseStatusEnum.RESEND.equals(res.getStatus())) {
                    // resend piece
                    logger.info("Sender[" + req.getRequestId() + "]: Resending piece");
                } else if (ResponseStatusEnum.DENY.equals(res.getStatus())) {
                    return;
                } else {
                    buffer = new byte[getPieceSize()];
                    placeInFile += read;
                    read = is.read(buffer);
                }
            }

            logger.info("Sender[" + req.getRequestId() + "]: Sending stop send");

            final DigestInputStream fiIs = is;

            jmsTemplate.send(sendDataDestination, new MessageCreator() {

                @Override
                public Message createMessage(Session session) throws JMSException {
                    Response stopSendResponse = new Response(ResponseStatusEnum.STOPSEND);
                    stopSendResponse.setHash(new String(fiIs.getMessageDigest().digest()));
                    return DomainMessageUtils.toResponseMessage(session, stopSendResponse);
                }

            });

            Message ackMessage = queueListener.getMessageInQueue();

            Object fromMessage = DomainMessageUtils.fromMessage(ackMessage);
            if (fromMessage instanceof Response) {
                Response ackResponse = (Response) fromMessage;
                if (ResponseStatusEnum.RESEND.equals(ackResponse.getStatus())) {
                    // TODO: resend the whole file
                }
            }

        } catch (Exception e) {
            throw new JmsFileTransferException(e);
        } finally {
            try {
                is.close();
            } catch (IOException ignored) {
            }
            if (queueListener != null)
                queueListener.close();
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected Message sendWithResponse(final MessageCreator mc, final Destination replyTo) {
        return (Message) jmsTemplate.execute(new SessionCallback() {

            @Override
            public Object doInJms(Session session) throws JMSException {
                DestinationRequestor requestor = null;
                try {
                    Message sendMessage = mc.createMessage(session);

                    requestor = new DestinationRequestor(session, replyTo, jmsTemplate.getReceiveTimeout());

                    Message returnMessage = requestor.request(sendMessage);

                    requestor.close();
                    return returnMessage;
                } finally {
                    if (requestor != null)
                        requestor.close();
                }
            }

        }, true);
    }

    protected Destination factoryDestination(Session session, String destinationName) throws JMSException {
        return jmsTemplate.getDestinationResolver().resolveDestinationName(session, destinationName, true);
    }

    public String getHashAlgorithm() {
        return hashAlgorithm;
    }

    public void setHashAlgorithm(String hashAlgorithm) {
        this.hashAlgorithm = hashAlgorithm;
    }
}