Coverage Summary for Class: MessageQueue (org.ethereum.net)

Class Method, % Line, %
MessageQueue 0% (0/14) 0% (0/47)
MessageQueue$1 0% (0/2) 0% (0/3)
MessageQueue$2 0% (0/2) 0% (0/6)
Total 0% (0/18) 0% (0/56)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2017 RSK Labs Ltd. 4  * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>) 5  * 6  * This program is free software: you can redistribute it and/or modify 7  * it under the terms of the GNU Lesser General Public License as published by 8  * the Free Software Foundation, either version 3 of the License, or 9  * (at your option) any later version. 10  * 11  * This program is distributed in the hope that it will be useful, 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14  * GNU Lesser General Public License for more details. 15  * 16  * You should have received a copy of the GNU Lesser General Public License 17  * along with this program. If not, see <http://www.gnu.org/licenses/>. 18  */ 19  20 package org.ethereum.net; 21  22 import io.netty.channel.ChannelFutureListener; 23 import io.netty.channel.ChannelHandlerContext; 24 import co.rsk.panic.PanicProcessor; 25 import org.ethereum.net.eth.message.EthMessage; 26 import org.ethereum.net.message.Message; 27 import org.ethereum.net.message.ReasonCode; 28 import org.ethereum.net.p2p.DisconnectMessage; 29 import org.ethereum.net.p2p.PingMessage; 30 import org.ethereum.net.server.Channel; 31 import org.slf4j.Logger; 32 import org.slf4j.LoggerFactory; 33  34 import java.util.Queue; 35 import java.util.concurrent.*; 36 import java.util.concurrent.atomic.AtomicInteger; 37  38 import static org.ethereum.net.message.StaticMessages.DISCONNECT_MESSAGE; 39  40 /** 41  * This class contains the logic for sending messages in a queue 42  * 43  * Messages open by send and answered by receive of appropriate message 44  * PING by PONG 45  * GET_PEERS by PEERS 46  * GET_TRANSACTIONS by TRANSACTIONS 47  * GET_BLOCK_HASHES by BLOCK_HASHES 48  * GET_BLOCKS by BLOCKS 49  * 50  * The following messages will not be answered: 51  * PONG, PEERS, HELLO, STATUS, TRANSACTIONS, BLOCKS 52  * 53  * @author Roman Mandeleil 54  */ 55 public class MessageQueue { 56  57  private static final Logger logger = LoggerFactory.getLogger("net"); 58  private static final PanicProcessor panicProcessor = new PanicProcessor(); 59  60  private static final ScheduledExecutorService timer = Executors.newScheduledThreadPool(4, new ThreadFactory() { 61  private AtomicInteger cnt = new AtomicInteger(0); 62  63  public Thread newThread(Runnable r) { 64  return new Thread(r, "MessageQueueTimer-" + cnt.getAndIncrement()); 65  } 66  }); 67  68  private Queue<MessageRoundtrip> requestQueue = new LinkedBlockingQueue<>(); 69  private Queue<MessageRoundtrip> respondQueue = new LinkedBlockingQueue<>(); 70  private ChannelHandlerContext ctx = null; 71  72  boolean hasPing = false; 73  private ScheduledFuture<?> timerTask; 74  private Channel channel; 75  76  public MessageQueue() { 77  } 78  79  public void activate(ChannelHandlerContext ctx) { 80  this.ctx = ctx; 81  timerTask = timer.scheduleAtFixedRate(new Runnable() { 82  public void run() { 83  try { 84  nudgeQueue(); 85  } catch (Throwable t) { 86  logger.error("Unhandled exception", t); 87  panicProcessor.panic("messagequeue", String.format("Unhandled exception %s", t.toString())); 88  } 89  } 90  }, 10, 10, TimeUnit.MILLISECONDS); 91  } 92  93  public void setChannel(Channel channel) { 94  this.channel = channel; 95  } 96  97  public void sendMessage(Message msg) { 98  if (msg instanceof PingMessage) { 99  if (hasPing) { 100  return; 101  } 102  logger.trace("Sending Ping Message to {}", channel); 103  hasPing = true; 104  } 105  106  Queue<MessageRoundtrip> queue = msg.getAnswerMessage() != null ? requestQueue : respondQueue; 107  queue.add(new MessageRoundtrip(msg)); 108  } 109  110  public void disconnect() { 111  disconnect(DISCONNECT_MESSAGE); 112  } 113  114  public void disconnect(ReasonCode reason) { 115  disconnect(new DisconnectMessage(reason)); 116  } 117  118  private void disconnect(DisconnectMessage msg) { 119  ctx.writeAndFlush(msg); 120  ctx.close(); 121  } 122  123  public void receivedMessage(Message msg) throws InterruptedException { 124  125  MessageRoundtrip messageRoundtrip = requestQueue.peek(); 126  if (messageRoundtrip != null) { 127  Message waitingMessage = messageRoundtrip.getMsg(); 128  129  if (waitingMessage instanceof PingMessage) { 130  hasPing = false; 131  } 132  133  if (waitingMessage.getAnswerMessage() != null 134  && msg.getClass() == waitingMessage.getAnswerMessage()) { 135  messageRoundtrip.answer(); 136  if (waitingMessage instanceof EthMessage) { 137  channel.getPeerStats().pong(messageRoundtrip.lastTimestamp); 138  } 139  logger.trace("Message round trip covered: [{}] ", 140  messageRoundtrip.getMsg().getClass()); 141  } 142  } 143  } 144  145  private void removeAnsweredMessage(MessageRoundtrip messageRoundtrip) { 146  if (messageRoundtrip != null && messageRoundtrip.isAnswered()) { 147  requestQueue.remove(); 148  } 149  } 150  151  private void nudgeQueue() { 152  // remove last answered message on the queue 153  removeAnsweredMessage(requestQueue.peek()); 154  // Now send the next message 155  sendToWire(respondQueue.poll()); 156  sendToWire(requestQueue.peek()); 157  } 158  159  private void sendToWire(MessageRoundtrip messageRoundtrip) { 160  161  if (messageRoundtrip != null && messageRoundtrip.getRetryTimes() == 0) { 162  // TODO: retry logic. See messageRoundtrip.hasToRetry 163  164  Message msg = messageRoundtrip.getMsg(); 165  166  ctx.writeAndFlush(msg).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); 167  168  if (msg.getAnswerMessage() != null) { 169  messageRoundtrip.incRetryTimes(); 170  messageRoundtrip.saveTime(); 171  } 172  } 173  } 174  175  public void close() { 176  if (timerTask != null) { 177  timerTask.cancel(false); 178  } 179  } 180 }