Java tutorial
/* * 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; } }