@Override public AbstractMessage handleIncomingMessage(AbstractMessage incomingMessage) { this.receivedMessagesAverage.inputValue(1); logger.info("in handleIncomingMessage(" + incomingMessage.toString() + ")"); // adds them to a list to see later which got stuck here incomingMessage.setReceiveTimeMillis(Clock.getMainClock().getTimeInMillis(false)); synchronized (receivedMessages) { this.receivedMessages.add(incomingMessage); } // handles the message AbstractMessage replyMessage = null; try { replyMessage = this.handleMessage(incomingMessage); } catch (InterruptedException e) { logger.debug("interrupted"); e.printStackTrace(); } synchronized (receivedMessages) { this.receivedMessages.remove(incomingMessage); } return replyMessage; }
@Override public void run() { while (running) { AbstractMessage message = null; try { message = pipeline.pollFirst(); // final AbstractMessage finalMessage = message; long t0 = Clock.getMainClock().getTimeInMillis(false); logger.debug("Got message: " + message); processMessage(message); logger.debug( "Done with message: " + message + ", took " + (Clock.getMainClock().getTimeInMillis(false) - t0) + " ms"); } catch (Exception e) { logger.error( "Exception processing message " + e.getMessage() + " caught, continuing with next message"); e.printStackTrace(); } finally { if (message != null) synchronized (message) { logger.debug("setting processed: " + message); message.setProcessed(); message.notify(); } } } }
@Override public void run() { try { // receivedMessagesAverage logger.info("receivedMessagesAverage: " + receivedMessagesAverage.getAverage()); // print which are unprocessed long currentTime = Clock.getMainClock().getTimeInMillis(false); String debug = ""; synchronized (receivedMessages) { for (AbstractMessage message : receivedMessages) debug += message.toString() + "@" + (message.getReceiveTimeMillis() - currentTime) + ":"; } if (logger.isDebugEnabled()) logger.debug("unprocessed messages: " + debug); } catch (Exception e) { // just so it doesn't die silently if an unhandled exception happened e.printStackTrace(); } }
public class DefaultIncomingMessageHandler implements IncomingMessageHandler { private static final Logger logger = LoggerFactory.getLogger(DefaultIncomingMessageHandler.class); protected final Tuner tuner; private final VideoSignaling videoSignaling; private final boolean freeride; private final Set<AbstractMessage> receivedMessages = new HashSet<AbstractMessage>(); private final MovingAverage receivedMessagesAverage = new MovingAverage(Clock.getMainClock(), 3, 1000); private final Map<PeerId, AtomicBoolean> peerLocks = new HashMap< PeerId, AtomicBoolean>(); // to enforce that only one UploadSlotRequest from a single peer is // granted at a time protected final MessageFlowControl flowControl = new MessageFlowControl(); private final Thread workerTuner; private final MessagePipeline pipelineTuner = new MessagePipeline(flowControl); private final Thread workerUploadSlotManager; private final MessagePipeline pipelineUploadSlotManager = new MessagePipeline(flowControl); private final BlockRequestPipeline pipelineBlockRequest = new BlockRequestPipeline(flowControl); // the workers for pipelineBlockRequest are the UploadSlots private boolean running = true; class MessageProcessor implements Runnable { private final SuperPriorityBlockingQueue<AbstractMessage> pipeline; public MessageProcessor(SuperPriorityBlockingQueue<AbstractMessage> pipeline) { this.pipeline = pipeline; } @Override public void run() { while (running) { AbstractMessage message = null; try { message = pipeline.pollFirst(); // final AbstractMessage finalMessage = message; long t0 = Clock.getMainClock().getTimeInMillis(false); logger.debug("Got message: " + message); processMessage(message); logger.debug( "Done with message: " + message + ", took " + (Clock.getMainClock().getTimeInMillis(false) - t0) + " ms"); } catch (Exception e) { logger.error( "Exception processing message " + e.getMessage() + " caught, continuing with next message"); e.printStackTrace(); } finally { if (message != null) synchronized (message) { logger.debug("setting processed: " + message); message.setProcessed(); message.notify(); } } } } } private Runnable statsReporter = new Runnable() { @Override public void run() { try { // receivedMessagesAverage logger.info("receivedMessagesAverage: " + receivedMessagesAverage.getAverage()); // print which are unprocessed long currentTime = Clock.getMainClock().getTimeInMillis(false); String debug = ""; synchronized (receivedMessages) { for (AbstractMessage message : receivedMessages) debug += message.toString() + "@" + (message.getReceiveTimeMillis() - currentTime) + ":"; } if (logger.isDebugEnabled()) logger.debug("unprocessed messages: " + debug); } catch (Exception e) { // just so it doesn't die silently if an unhandled exception happened e.printStackTrace(); } } }; private ScheduledFuture<?> statsReporterScheduledFuture; public DefaultIncomingMessageHandler( final Tuner tuner, final VideoSignaling videoSignaling, final boolean freeride) { this.tuner = tuner; this.videoSignaling = videoSignaling; this.freeride = freeride; this.statsReporterScheduledFuture = ExecutorPool.getScheduledExecutorService() .scheduleAtFixedRate(statsReporter, 1000, 1000, TimeUnit.MILLISECONDS); this.workerTuner = new Thread(new MessageProcessor(this.pipelineTuner)); this.workerTuner.setName("MessageProcessor-Tuner"); this.workerUploadSlotManager = new Thread(new MessageProcessor(this.pipelineUploadSlotManager)); this.workerUploadSlotManager.setName("MessageProcessor-USM"); this.workerTuner.start(); this.workerUploadSlotManager.start(); this.videoSignaling .getUploadSlotManager() .registerPipelineBlockRequest(this.pipelineBlockRequest); } @Override public void shutdown() { this.running = false; this.statsReporterScheduledFuture.cancel(false); } /* @Override public AbstractMessage handleIncomingMessage(final P2PAddress senderP2pAddress, final byte[] incomingByteArray, final int offset) { AbstractMessage incomingMessage = this.getMessage(senderP2pAddress, incomingByteArray, offset); return handleIncomingMessage(incomingMessage); } @Override public AbstractMessage handleIncomingMessage(PeerId peerIdSender, byte[] incomingByteArray, int offset) { AbstractMessage incomingMessage = this.getMessage(peerIdSender, incomingByteArray, offset); return handleIncomingMessage(incomingMessage); } */ @Override public AbstractMessage handleIncomingMessage(AbstractMessage incomingMessage) { this.receivedMessagesAverage.inputValue(1); logger.info("in handleIncomingMessage(" + incomingMessage.toString() + ")"); // adds them to a list to see later which got stuck here incomingMessage.setReceiveTimeMillis(Clock.getMainClock().getTimeInMillis(false)); synchronized (receivedMessages) { this.receivedMessages.add(incomingMessage); } // handles the message AbstractMessage replyMessage = null; try { replyMessage = this.handleMessage(incomingMessage); } catch (InterruptedException e) { logger.debug("interrupted"); e.printStackTrace(); } synchronized (receivedMessages) { this.receivedMessages.remove(incomingMessage); } return replyMessage; } @Override public AbstractMessage getMessage( final PeerId peerIdSender, final byte[] incomingByteArray, final int offset) { try { return this.videoSignaling .getMessageFactory() .getMessage(peerIdSender, incomingByteArray, offset); } catch (UnknownHostException e) { String byteArrayAsString = "["; for (byte b : incomingByteArray) byteArrayAsString += b + ","; byteArrayAsString += "]"; logger.error( "Error processing incoming message, dropping: " + e.getMessage() + ", message is: " + byteArrayAsString + " with offset " + offset); e.printStackTrace(); return null; } } @Override public AbstractMessage getMessage( final P2PAddress senderP2pAddress, final byte[] incomingByteArray, final int offset) { AbstractMessage incomingMessage = null; int countDown = 5; while (incomingMessage == null && countDown-- > 0) { try { incomingMessage = this.videoSignaling .getMessageFactory() .getMessage(senderP2pAddress, incomingByteArray, offset); } catch (UnknownHostException e1) { logger.error("Message received from unknown host -- dropping"); e1.printStackTrace(); return null; } catch (SenderNotFoundException e) { if (logger.isDebugEnabled()) logger.debug( "sender of message not found. it could be that the message containing the sender is a bit delayed. will wait and retry."); try { Thread.sleep(50); } catch (InterruptedException e1) { if (logger.isDebugEnabled()) logger.debug("interrupted"); break; } } catch (Exception e) { String byteArrayAsString = "["; for (byte b : incomingByteArray) byteArrayAsString += b + ","; byteArrayAsString += "]"; logger.error( "Error processing incoming message, dropping: " + e.getMessage() + ", message is: " + byteArrayAsString + " with offset " + offset); e.printStackTrace(); return null; } } return incomingMessage; } protected AbstractMessage handleMessage(AbstractMessage incomingMessage) throws InterruptedException { // adds message to the right pipeline // BR if (incomingMessage instanceof BlockRequestMessage) { BlockRequestMessage blockRequestMessage = (BlockRequestMessage) incomingMessage; // stats this.videoSignaling.getStats().newIncomingRequest(blockRequestMessage); this.pipelineBlockRequest.offer(incomingMessage); } // USM else if (incomingMessage instanceof SubscribeMessage || incomingMessage instanceof InterestedMessage || incomingMessage instanceof DisconnectMessage && ((DisconnectMessage) incomingMessage).stopUploading()) this.pipelineUploadSlotManager.offer(incomingMessage); // TUNER else if (incomingMessage instanceof PingMessage || incomingMessage instanceof HaveMessage || incomingMessage instanceof SubscribedMessage || incomingMessage instanceof QueuedMessage || incomingMessage instanceof GrantedMessage || incomingMessage instanceof BlockReplyMessage || incomingMessage instanceof PeerSuggestionMessage || incomingMessage instanceof DisconnectMessage && ((DisconnectMessage) incomingMessage).stopDownloading()) this.pipelineTuner.offer(incomingMessage); else { logger.error( "Message of type (" + incomingMessage.getClass().getCanonicalName() + ", " + incomingMessage.toString() + ") is not recognized"); // flow control needs to take its MID not to have a gap this.pipelineUploadSlotManager.offer(incomingMessage); return null; } AbstractMessage replyMessage = null; try { replyMessage = this.waitForProcessingGetReply(incomingMessage); } catch (Exception e) { e.printStackTrace(); logger.error("Error waiting for message to be processed: " + e.getMessage()); } return replyMessage; } protected AbstractMessage waitForProcessingGetReply(AbstractMessage incomingMessage) throws InterruptedException { // waits for message to be processed synchronized (incomingMessage) { while (!incomingMessage.isProcessed()) { logger.debug("waiting on: " + incomingMessage); incomingMessage.wait(); } } if (logger.isDebugEnabled()) { logger.debug("done waiting on: " + incomingMessage); } AbstractMessage replyMessage = incomingMessage.getReplyMessage(); if (logger.isDebugEnabled()) { if (replyMessage == null) { logger.debug("will reply NULL to " + incomingMessage); } else { logger.debug("will reply: " + replyMessage + " to " + incomingMessage); } } return replyMessage; } protected void processMessage(AbstractMessage incomingMessage) { if (incomingMessage.isProcessed() && !(incomingMessage instanceof HaveMessage || incomingMessage instanceof BlockReplyMessage)) return; AbstractMessage replyMessage = null; // USM if (incomingMessage instanceof SubscribeMessage) { replyMessage = this.handleSubscribe((SubscribeMessage) incomingMessage); } else if (incomingMessage instanceof InterestedMessage) { replyMessage = this.handleInterested((InterestedMessage) incomingMessage); } else if (incomingMessage instanceof DisconnectMessage) { DisconnectMessage disconnectMessage = (DisconnectMessage) incomingMessage; if (disconnectMessage.stopUploading()) this.handleDisconnect(disconnectMessage); // TUNER if (disconnectMessage.stopDownloading()) this.handleDisconnect(disconnectMessage); } else if (incomingMessage instanceof HaveMessage) { this.handleHave((HaveMessage) incomingMessage); } else if (incomingMessage instanceof SubscribedMessage) { this.handleSubscribed((SubscribedMessage) incomingMessage); } else if (incomingMessage instanceof QueuedMessage) { this.handleQueued((QueuedMessage) incomingMessage); } else if (incomingMessage instanceof GrantedMessage) { this.handleGranted((GrantedMessage) incomingMessage); } else if (incomingMessage instanceof BlockReplyMessage) { this.handleBlockReply((BlockReplyMessage) incomingMessage); } else if (incomingMessage instanceof PeerSuggestionMessage) { this.handlePeerSuggestion((PeerSuggestionMessage) incomingMessage); } else { logger.warn("Unknown message received, not processing (" + incomingMessage + ")"); } incomingMessage.setReplyMessage(replyMessage, incomingMessage.getSender()); } protected AbstractMessage handlePing(PingMessage incomingMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + incomingMessage.toString()); if (incomingMessage.isRequest()) return this.videoSignaling.getPingReply(incomingMessage); else return null; } protected AbstractMessage handleSubscribe(final SubscribeMessage incomingMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + incomingMessage.toString()); SegmentIdentifier segmentIdentifier = incomingMessage.getSegmentIdentifier(); Segment segment = this.tuner.getSegmentStorage().getSegment(segmentIdentifier); if (segment == null) { logger.warn("USRM:" + incomingMessage.toString() + " asks for unknown segment."); return this.videoSignaling.getSubscribed( segmentIdentifier, null, 0, incomingMessage.getSender(), false); } AtomicBoolean peerLock; synchronized (this.peerLocks) { peerLock = this.peerLocks.get(incomingMessage.getSender()); if (peerLock == null) { this.peerLocks.put(incomingMessage.getSender(), peerLock = new AtomicBoolean(false)); } } Subscriber subscriber = new Subscriber( incomingMessage.getSender(), segmentIdentifier, segment, this.videoSignaling.getUploadSlotManager(), peerLock); // lets the tuner know: it may be a new interesting neighbor this.tuner .getNeighborList() .addPotentialCandidate(segmentIdentifier, incomingMessage.getSender()); // checks if already granted int timeoutMillis = this.videoSignaling.getUploadSlotManager().getUploadSlotTimeoutMillis(subscriber); boolean isAlreadyGranted = timeoutMillis > 0; if (isAlreadyGranted) logger.warn( "USR:"******" has already an upload slot! the other peer could have a bug (requesting again when it's not supposed to). But it could also that a message got lost, or the peer failed and came back."); // if not, tries to add to queue and gets timeout if (!isAlreadyGranted) timeoutMillis = this.videoSignaling.getUploadSlotManager().addSubscriberGetTimeoutMillis(subscriber); if (logger.isDebugEnabled()) logger.debug( "addUploadSlotRequest [" + subscriber + "] returned [" + timeoutMillis + "," + isAlreadyGranted + "]"); if (isAlreadyGranted) return this.videoSignaling.getGranted( segmentIdentifier, timeoutMillis, UploadSlot.INACTIVITY_TIMEOUT_S * 1000, incomingMessage.getSender(), false); else if (timeoutMillis > 0) { // replies with the timeout, so the other peer knows when to send another request SegmentBlockMap segmentBlockMap = this.tuner.getSegmentBlockMap(segmentIdentifier); return this.videoSignaling.getSubscribed( segmentIdentifier, segmentBlockMap, timeoutMillis, incomingMessage.getSender(), false); } else { // queue is full or something like that return this.videoSignaling.getSubscribed( segmentIdentifier, null, 0, incomingMessage.getSender(), true); } } protected void handleSubscribed(final SubscribedMessage subscribedMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + subscribedMessage.toString()); if (subscribedMessage.isNot()) { this.tuner .getNeighborList() .removeNeighbor( subscribedMessage.getSender(), subscribedMessage.getSegmentIdentifier(), 5000); // this.tuner.getNeighborList().lookForCandidates(usReplyMessage.getSegmentIdentifier()); } else { // registers timeout for not trying again too soon (if peer has slot, does not change // anything, only if it were BACK_IN_Q) this.tuner .getNeighborList() .addNeighbor( subscribedMessage.getSender(), subscribedMessage.getSegmentIdentifier(), subscribedMessage.getSegmentBlockMap(), subscribedMessage.getTimeoutMillis()); } } protected void handleGranted(final GrantedMessage grantedMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + grantedMessage.toString()); if (grantedMessage.isNot()) { this.tuner .getNeighborList() .removeUsGranter( grantedMessage.getSender(), grantedMessage.getSegmentIdentifier(), grantedMessage.getTimeoutMillis()); } else this.tuner .getNeighborList() .addUsGranter( grantedMessage.getSender(), grantedMessage.getSegmentIdentifier(), grantedMessage.getTimeoutMillis()); } protected void handleQueued(final QueuedMessage queuedMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + queuedMessage.toString()); if (queuedMessage.isNot()) { this.tuner .getNeighborList() .removeFromInterested( queuedMessage.getSender(), queuedMessage.getSegmentIdentifier(), queuedMessage.getTimeoutMillis()); } else { this.tuner .getNeighborList() .addNeighbor( queuedMessage.getSender(), queuedMessage.getSegmentIdentifier(), null, queuedMessage.getTimeoutMillis()); } } protected AbstractMessage handleInterested(final InterestedMessage incomingMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + incomingMessage.toString()); Subscriber subscriber; if (null != (subscriber = this.videoSignaling .getUploadSlotManager() .getGrantedUploadSlotRequest( incomingMessage.getSender(), incomingMessage.getSegmentIdentifier()))) { logger.warn(subscriber + " is already granted!!"); return this.videoSignaling.getGranted( incomingMessage.getSegmentIdentifier(), subscriber.getTimeToTimeoutMillis(), UploadSlot.INACTIVITY_TIMEOUT_S * 1000, incomingMessage.getSender(), false); } int timeoutMillis = this.videoSignaling .getUploadSlotManager() .setInterestedGetTimeout( incomingMessage.getSender(), incomingMessage.getSegmentIdentifier(), !incomingMessage.isNot()); if (incomingMessage.isNot()) { // if not interested, should return subscribed return this.videoSignaling.getSubscribed( incomingMessage.getSegmentIdentifier(), null, timeoutMillis, incomingMessage.getSender(), false); } else { // must return Queued with new (extended) timeout.. and synchronize with the possible Granted return this.videoSignaling.getQueued( incomingMessage.getSegmentIdentifier(), timeoutMillis, incomingMessage.getSender(), false); } } protected void handleBlockReply(BlockReplyMessage blockReplyMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + blockReplyMessage.toString()); // stats this.videoSignaling.getStats().updateOutgoingRequest(blockReplyMessage); final SegmentBlock segmentBlock = blockReplyMessage.getSegmentBlock(); PeerId sender = blockReplyMessage.getSender(); // Make sure we never save anything empty if (blockReplyMessage.getReplyCode() == BlockReplyCode.GRANTED && segmentBlock != null) { // increases reputation, resets timeout this.tuner.getNeighborList().registerSuccess(sender, segmentBlock); // increments application-level hop count if (segmentBlock != null) { segmentBlock.setDownloadTime(); segmentBlock.incrementHopCount(); } // writes segment (important!) final boolean successPut = this.tuner.putSegmentBlock(sender, segmentBlock); if (successPut) { Runnable runner = new Runnable() { @Override public void run() { try { // signals player that it has arrived (maybe it's waiting for it) if (tuner.getVideoPlayer() != null) tuner.getVideoPlayer().tryAndPlay(); // sends HAVE messages videoSignaling.sendHave( segmentBlock.getSegmentIdentifier(), segmentBlock.getBlockNumber()); } catch (Exception e) { // just so it doesn't die silently if an unhandled exception happened logger.error("error calling player or sending Haves: " + e.getMessage()); e.printStackTrace(); } } }; ExecutorPool.getGeneralExecutorService().execute(runner); } } else if (blockReplyMessage.getReplyCode() == BlockReplyCode.REJECTED || blockReplyMessage.getReplyCode() == BlockReplyCode.DONT_HAVE || blockReplyMessage.getReplyCode() == BlockReplyCode.NO_SLOT) { // no slot granted, so it probably missed the message ungranting the slot (or message could be // on its way when it lost slot) if (blockReplyMessage.getReplyCode() == BlockReplyCode.NO_SLOT) this.tuner .getNeighborList() .removeUsGranter( blockReplyMessage.getSender(), blockReplyMessage.getSegmentBlock().getSegmentIdentifier(), 10000); // DONT_HAVE is the same as HAVE (!doHave) // except that a peer keeps track if many requests are failing in order to send unsubscribe // (hopefully eventually solving the sync issue) // ===> DISABLED BECAUSE IT COULD MAKE A PEER STALL FOR A LONG TIME // else if (blockReplyMessage.getReplyCode()==BlockReplyCode.DONT_HAVE) // this.tuner.updateNeighborBlockMap(blockReplyMessage.getSender(), // blockReplyMessage.getSegmentBlock().getSegmentIdentifier(), // blockReplyMessage.getSegmentBlock().getBlockNumber(), false, -1); // anyway, a block request has failed, so let's react quickly and reschedule the block if (segmentBlock != null && segmentBlock.getBlockNumber() != -1) this.tuner .getNeighborList() .registerFailure( segmentBlock.getSegmentIdentifier(), segmentBlock.getBlockNumber(), blockReplyMessage.getSender()); } else { logger.warn("unexpected format of message: " + blockReplyMessage); } // TODO: see what to do when getReplyCode() is different or segmentBlock==null } protected void handleHave(final HaveMessage incomingMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + incomingMessage.toString()); // updates block map of peer this.tuner.updateNeighborBlockMap( incomingMessage.getSender(), incomingMessage.getSegmentIdentifier(), incomingMessage.getBlockNumber(), incomingMessage.doHave(), incomingMessage.getRate()); } protected void handleDisconnect(final DisconnectMessage incomingMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + incomingMessage.toString()); if (incomingMessage.stopUploading()) { // this peer won't upload anymore to the sender of the message (peer may be added again) this.videoSignaling.disconnectPeer( incomingMessage.getSender(), incomingMessage.getSegmentIdentifier()); } if (incomingMessage.stopDownloading()) { // this peer won't download anymore from the sender of the message (peer may be added again) this.tuner.disconnectPeer( incomingMessage.getSender(), incomingMessage.getSegmentIdentifier()); // this.tuner.getNeighborList().lookForCandidates(incomingMessage.getSegmentIdentifier()); } } private void handlePeerSuggestion(PeerSuggestionMessage incomingMessage) { if (logger.isDebugEnabled()) logger.debug("processing message:" + incomingMessage.toString()); for (PeerId peerId : incomingMessage.getSuggestedPeerIds()) { this.tuner .getNeighborList() .addPotentialCandidate(incomingMessage.getSegmentIdentifier(), peerId); } } }