MessageRouterImpl.java

Index Score
com.limegroup.gnutella
FrostWire

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
LINE_COMMENTNumber of line comments
EXEC_COMMENTSComments in executable code
PARAMSNumber of formal parameter declarations
SIZESize of the file in bytes
INTERFACE_COMPLEXITYInterface complexity
DECL_COMMENTSComments in declarations
EXITSProcedure exits
CYCLOMATICCyclomatic complexity
UNIQUE_OPERANDSNumber of unique operands
LINESNumber of lines in the source file
PROGRAM_LENGTHHalstead program length
OPERATORSNumber of operators
OPERANDSNumber of operands
ELOCEffective lines of code
JAVA0034JAVA0034 Missing braces in if statement
COMMENTSComment lines
RETURNSNumber of return points from functions
PROGRAM_VOCABHalstead program vocabulary
LOGICAL_LINESNumber of statements
LOCLines of code
COMPARISONSNumber of comparison operators
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
FUNCTIONSNumber of function declarations
BLOCKSNumber of blocks
DOC_COMMENTNumber of javadoc comment lines
WHITESPACENumber of whitespace lines
LOOPSNumber of loops
JAVA0144JAVA0144 Line exceeds maximum M characters
JAVA0076JAVA0076 Use of magic number
JAVA0020JAVA0020 Field name does not have required form
JAVA0143JAVA0143 Synchronized method
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
JAVA0035JAVA0035 Missing braces in for statement
BLOCK_COMMENTNumber of block comment lines
JAVA0264JAVA0264 Integer math in long context - check for overflow
JAVA0128JAVA0128 Public constructor in non-public class
JAVA0177JAVA0177 Variable declaration missing initializer
JAVA0173JAVA0173 Unused method parameter
JAVA0163JAVA0163 Empty statement
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0130JAVA0130 Non-static method does not use instance fields
UNIQUE_OPERATORSNumber of unique operators
JAVA0109JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA0087JAVA0087 Use of Thread.sleep()
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0137JAVA0137 Non-abstract class missing constructor
JAVA0008JAVA0008 Empty catch block
JAVA0111JAVA0111 Incorrect javadoc: @return tag for void method
PROGRAM_VOLUMEHalstead program volume
JAVA0118JAVA0118 Missing javadoc: type 'type'
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0174JAVA0174 Assigned local variable never used
JAVA0138JAVA0138 N parameters defined for method (maximum: M)
JAVA0043JAVA0043 Inner class does not use outer class
JAVA0030JAVA0030 Private field not used
JAVA0100JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0022JAVA0022 Static final field name does not have required form
JAVA0068JAVA0068 Modifiers not declared in recommended order
JAVA0256JAVA0256 Assignment of external collection/array to field
NEST_DEPTHMaximum nesting depth
package com.limegroup.gnutella; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.collection.Buffer; import org.limewire.collection.FixedsizeHashMap; import org.limewire.collection.NoMoreStorageException; import org.limewire.concurrent.ExecutorsHelper; import org.limewire.concurrent.ManagedThread; import org.limewire.inspection.Inspectable; import org.limewire.inspection.InspectionPoint; import org.limewire.io.IOUtils; import org.limewire.io.IpPort; import org.limewire.io.NetworkUtils; import org.limewire.net.SocketsManager; import org.limewire.net.SocketsManager.ConnectType; import org.limewire.security.AddressSecurityToken; import org.limewire.security.MACCalculatorRepositoryManager; import org.limewire.security.SecurityToken; import org.limewire.service.ErrorService; import org.limewire.setting.evt.SettingEvent; import org.limewire.setting.evt.SettingListener; import org.limewire.util.Base32; import org.limewire.util.ByteOrder; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; import com.limegroup.gnutella.auth.ContentManager; import com.limegroup.gnutella.connection.Connection; import com.limegroup.gnutella.connection.ConnectionLifecycleEvent; import com.limegroup.gnutella.connection.ConnectionLifecycleListener; import com.limegroup.gnutella.connection.RoutedConnection; import com.limegroup.gnutella.dht.DHTManager; import com.limegroup.gnutella.guess.GUESSEndpoint; import com.limegroup.gnutella.guess.OnDemandUnicaster; import com.limegroup.gnutella.messagehandlers.DualMessageHandler; import com.limegroup.gnutella.messagehandlers.InspectionRequestHandler; import com.limegroup.gnutella.messagehandlers.MessageHandler; import com.limegroup.gnutella.messagehandlers.OOBHandler; import com.limegroup.gnutella.messagehandlers.UDPCrawlerPingHandler; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.PingReply; import com.limegroup.gnutella.messages.PingReplyFactory; import com.limegroup.gnutella.messages.PingRequest; import com.limegroup.gnutella.messages.PingRequestFactory; import com.limegroup.gnutella.messages.PushRequest; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryReplyFactory; import com.limegroup.gnutella.messages.QueryRequest; import com.limegroup.gnutella.messages.QueryRequestFactory; import com.limegroup.gnutella.messages.StaticMessages; import com.limegroup.gnutella.messages.vendor.ContentResponse; import com.limegroup.gnutella.messages.vendor.DHTContactsMessage; import com.limegroup.gnutella.messages.vendor.HeadPing; import com.limegroup.gnutella.messages.vendor.HeadPong; import com.limegroup.gnutella.messages.vendor.HeadPongFactory; import com.limegroup.gnutella.messages.vendor.HopsFlowVendorMessage; import com.limegroup.gnutella.messages.vendor.InspectionRequest; import com.limegroup.gnutella.messages.vendor.LimeACKVendorMessage; import com.limegroup.gnutella.messages.vendor.PushProxyAcknowledgement; import com.limegroup.gnutella.messages.vendor.PushProxyRequest; import com.limegroup.gnutella.messages.vendor.QueryStatusResponse; import com.limegroup.gnutella.messages.vendor.ReplyNumberVendorMessage; import com.limegroup.gnutella.messages.vendor.SimppRequestVM; import com.limegroup.gnutella.messages.vendor.SimppVM; import com.limegroup.gnutella.messages.vendor.TCPConnectBackRedirect; import com.limegroup.gnutella.messages.vendor.TCPConnectBackVendorMessage; import com.limegroup.gnutella.messages.vendor.UDPConnectBackRedirect; import com.limegroup.gnutella.messages.vendor.UDPConnectBackVendorMessage; import com.limegroup.gnutella.messages.vendor.UDPCrawlerPing; import com.limegroup.gnutella.messages.vendor.UpdateRequest; import com.limegroup.gnutella.messages.vendor.UpdateResponse; import com.limegroup.gnutella.messages.vendor.VendorMessage; import com.limegroup.gnutella.routing.PatchTableMessage; import com.limegroup.gnutella.routing.QueryRouteTable; import com.limegroup.gnutella.routing.ResetTableMessage; import com.limegroup.gnutella.routing.RouteTableMessage; import com.limegroup.gnutella.search.QueryDispatcher; import com.limegroup.gnutella.search.QueryHandler; import com.limegroup.gnutella.search.QueryHandlerFactory; import com.limegroup.gnutella.search.ResultCounter; import com.limegroup.gnutella.search.SearchResultHandler; import com.limegroup.gnutella.settings.ConnectionSettings; import com.limegroup.gnutella.settings.DownloadSettings; import com.limegroup.gnutella.settings.FilterSettings; import com.limegroup.gnutella.settings.MessageSettings; import com.limegroup.gnutella.settings.SearchSettings; import com.limegroup.gnutella.simpp.SimppManager; import com.limegroup.gnutella.util.LimeWireUtils; import com.limegroup.gnutella.version.UpdateHandler; /** * One of the three classes that make up the core of the backend. This * class' job is to direct the routing of messages and to count those message * as they pass through. To do so, it aggregates a ConnectionManager that * maintains a list of connections. */ //@Singleton // not here because it's not constructed, subclass is! public abstract class MessageRouterImpl implements MessageRouter { private static final Log LOG = LogFactory.getLog(MessageRouterImpl.class); /** * Constant for the number of old connections to use when forwarding * traffic from old connections. */ private static final int OLD_CONNECTIONS_TO_USE = 15; /** * The GUID we attach to QueryReplies to allow PushRequests in * responses. */ protected byte[] _clientGUID; /** * The maximum size for <tt>RouteTable</tt>s. */ private int MAX_ROUTE_TABLE_SIZE = 50000; //actually 100,000 entries /** * Maps PingRequest GUIDs to PingReplyHandlers. Stores 2-4 minutes, * typically around 2500 entries, but never more than 100,000 entries. */ @InspectionPoint("ping routing table dump") private RouteTable _pingRouteTable = new RouteTable(2*60, MAX_ROUTE_TABLE_SIZE); /** * Maps QueryRequest GUIDs to QueryReplyHandlers. Stores 5-10 minutes, * typically around 13000 entries, but never more than 100,000 entries. */ @InspectionPoint("query routing table dump") private RouteTable _queryRouteTable = new RouteTable(5*60, MAX_ROUTE_TABLE_SIZE); /** * Maps QueryReply client GUIDs to PushRequestHandlers. Stores 7-14 * minutes, typically around 3500 entries, but never more than 100,000 * entries. */ @InspectionPoint("push routing table dump") private RouteTable _pushRouteTable = new RouteTable(7*60, MAX_ROUTE_TABLE_SIZE); /** * Maps HeadPong guids to the originating pingers. Short-lived since * we expect replies from our leaves quickly. */ @InspectionPoint("headpong routing table dump") private RouteTable _headPongRouteTable = new RouteTable(10, MAX_ROUTE_TABLE_SIZE); /** How long to buffer up out-of-band replies. */ private static final long CLEAR_TIME = 30 * 1000; // 30 seconds /** * The amount of time after which to expire an OOBSession. */ private static final long OOB_SESSION_EXPIRE_TIME = 2 * 60 * 1000; /** Time between sending HopsFlow messages. */ private static final long HOPS_FLOW_INTERVAL = 15 * 1000; // 15 seconds /** The maximum number of UDP replies to buffer up. Non-final for * testing. */ public static final int MAX_BUFFERED_REPLIES = 250; private int maxBufferedReplies = MAX_BUFFERED_REPLIES; /** * Keeps track of QueryReplies to be sent after recieving LimeAcks (sent * if the sink wants them). Cleared every CLEAR_TIME seconds. * TimedGUID->QueryResponseBundle. */ private final Map<GUID.TimedGUID, QueryResponseBundle> _outOfBandReplies = new Hashtable<GUID.TimedGUID, QueryResponseBundle>(); private final BypassedResultsCache _bypassedResultsCache; /** * Keeps track of what hosts we have recently tried to connect back to via * UDP. The size is limited and once the size is reached, no more connect * back attempts will be honored. */ private static final FixedsizeHashMap<String, String> _udpConnectBacks = new FixedsizeHashMap<String, String>(200); /** * The maximum numbers of ultrapeers to forward a UDPConnectBackRedirect * message to, per forward. */ private static final int MAX_UDP_CONNECTBACK_FORWARDS = 5; /** * Keeps track of what hosts we have recently tried to connect back to via * TCP. The size is limited and once the size is reached, no more connect * back attempts will be honored. */ private static final FixedsizeHashMap<String, String> _tcpConnectBacks = new FixedsizeHashMap<String, String>(200); /** * The maximum numbers of ultrapeers to forward a TCPConnectBackRedirect * message to, per forward. */ private static final int MAX_TCP_CONNECTBACK_FORWARDS = 5; /** * The processingqueue to add tcpconnectback socket connections to. */ private static final ExecutorService TCP_CONNECT_BACKER = ExecutorsHelper.newProcessingQueue("TCPConnectBack"); /** * A handle to the thread that deals with QRP Propagation */ private final QRPPropagator QRP_PROPAGATOR = new QRPPropagator(); /** * Variable for the most recent <tt>QueryRouteTable</tt> created * for this node. If this node is an Ultrapeer, the routing * table will include the tables from its leaves. */ private QueryRouteTable _lastQueryRouteTable; /** * The maximum number of response to send to a query that has * a "high" number of hops. */ private static final int HIGH_HOPS_RESPONSE_LIMIT = 10; /** * The lifetime of OOBs guids. */ private static final long TIMED_GUID_LIFETIME = 25 * 1000; /** Keeps track of Listeners of GUIDs. */ private volatile Map<byte[], List<MessageListener>> _messageListeners = Collections.emptyMap(); /** * Lock that registering & unregistering listeners can hold * while replacing the listeners map / lists. */ private final Object MESSAGE_LISTENER_LOCK = new Object(); /** * The time we last received a request for a query key. */ private long _lastQueryKeyTime; /** Handlers for TCP messages. */ private ConcurrentMap<Class<? extends Message>, MessageHandler> messageHandlers = new ConcurrentHashMap<Class<? extends Message>, MessageHandler>(30, 0.75f, 3); /** Handler for UDP messages. */ private ConcurrentMap<Class<? extends Message>, MessageHandler> udpMessageHandlers = new ConcurrentHashMap<Class<? extends Message>, MessageHandler>(15, 0.75f, 3); /** Handler for TCP messages. */ private ConcurrentMap<Class<? extends Message>, MessageHandler> multicastMessageHandlers = new ConcurrentHashMap<Class<? extends Message>, MessageHandler>(5, 0.75f, 3); /** The length of time a multicast guid should stay alive. */ private static final long MULTICAST_GUID_EXPIRE_TIME = 60 * 1000; /** How long to remember cached udp reply handlers. */ private static final int UDP_REPLY_CACHE_TIME = 60 * 1000; @InspectionPoint("guid tracker") @SuppressWarnings("unused") private final GUIDTracker guidTracker = new GUIDTracker(); @InspectionPoint("dropped replies") private final DroppedReplyCounter droppedReplyCounter = new DroppedReplyCounter(); @InspectionPoint("duplicate queries") private final DuplicateQueryCounter duplicateQueryCounter = new DuplicateQueryCounter(); protected final NetworkManager networkManager; protected final QueryRequestFactory queryRequestFactory; protected final QueryHandlerFactory queryHandlerFactory; protected final OnDemandUnicaster onDemandUnicaster; protected final HeadPongFactory headPongFactory; protected final PingReplyFactory pingReplyFactory; protected final ConnectionManager connectionManager; protected final ReplyHandler forMeReplyHandler; protected final QueryUnicaster queryUnicaster; protected final FileManager fileManager; protected final ContentManager contentManager; protected final DHTManager dhtManager; protected final UploadManager uploadManager; protected final DownloadManager downloadManager; protected final UDPService udpService; protected final SearchResultHandler searchResultHandler; protected final SocketsManager socketsManager; protected final HostCatcher hostCatcher; protected final QueryReplyFactory queryReplyFactory; protected final StaticMessages staticMessages; protected final Provider<MessageDispatcher> messageDispatcher; protected final MulticastService multicastService; protected final QueryDispatcher queryDispatcher; protected final Provider<ActivityCallback> activityCallback; protected final ConnectionServices connectionServices; protected final ScheduledExecutorService backgroundExecutor; protected final Provider<PongCacher> pongCacher; protected final Provider<SimppManager> simppManager; protected final Provider<UpdateHandler> updateHandler; protected final UDPReplyHandlerCache udpReplyHandlerCache; protected final GuidMap multicastGuidMap; private final Provider<InspectionRequestHandler> inspectionRequestHandlerFactory; private final Provider<UDPCrawlerPingHandler> udpCrawlerPingHandlerFactory; private final Provider<OOBHandler> oobHandlerFactory; private final Provider<MACCalculatorRepositoryManager> MACCalculatorRepositoryManager; private final PingRequestFactory pingRequestFactory; private final MessageHandlerBinder messageHandlerBinder; private final ConnectionListener connectionListener = new ConnectionListener(); /** * Creates a MessageRouter. Must call initialize before using. */ @Inject // TODO: Create a MessageRouterController, // or split MessageRouter into a number of different classes, // instead of passing all of these... protected MessageRouterImpl(NetworkManager networkManager, QueryRequestFactory queryRequestFactory, QueryHandlerFactory queryHandlerFactory, OnDemandUnicaster onDemandUnicaster, HeadPongFactory headPongFactory, PingReplyFactory pingReplyFactory, ConnectionManager connectionManager, @Named("forMeReplyHandler") ReplyHandler forMeReplyHandler, QueryUnicaster queryUnicaster, FileManager fileManager, ContentManager contentManager, DHTManager dhtManager, UploadManager uploadManager, DownloadManager downloadManager, UDPService udpService, SearchResultHandler searchResultHandler, SocketsManager socketsManager, HostCatcher hostCatcher, QueryReplyFactory queryReplyFactory, StaticMessages staticMessages, Provider<MessageDispatcher> messageDispatcher, MulticastService multicastService, QueryDispatcher queryDispatcher, Provider<ActivityCallback> activityCallback, ConnectionServices connectionServices, ApplicationServices applicationServices, @Named("backgroundExecutor") ScheduledExecutorService backgroundExecutor, Provider<PongCacher> pongCacher, Provider<SimppManager> simppManager, Provider<UpdateHandler> updateHandler, GuidMapManager guidMapManager, UDPReplyHandlerCache udpReplyHandlerCache, Provider<InspectionRequestHandler> inspectionRequestHandlerFactory, Provider<UDPCrawlerPingHandler> udpCrawlerPingHandlerFactory, PingRequestFactory pingRequestFactory, MessageHandlerBinder messageHandlerBinder, Provider<OOBHandler> oobHandlerFactory, Provider<MACCalculatorRepositoryManager> MACCalculatorRepositoryManager) { this.networkManager = networkManager; this.queryRequestFactory = queryRequestFactory; this.queryHandlerFactory = queryHandlerFactory; this.onDemandUnicaster = onDemandUnicaster; this.headPongFactory = headPongFactory; this.pingReplyFactory = pingReplyFactory; this.connectionManager = connectionManager; this.forMeReplyHandler = forMeReplyHandler; this.queryUnicaster = queryUnicaster; this.fileManager = fileManager; this.contentManager = contentManager; this.dhtManager = dhtManager; this.uploadManager = uploadManager; this.downloadManager = downloadManager; this.udpService = udpService; this.searchResultHandler = searchResultHandler; this.socketsManager = socketsManager; this.hostCatcher = hostCatcher; this.queryReplyFactory = queryReplyFactory; this.staticMessages = staticMessages; this.messageDispatcher = messageDispatcher; this.multicastService = multicastService; this.queryDispatcher = queryDispatcher; this.activityCallback = activityCallback; this.connectionServices = connectionServices; this.backgroundExecutor = backgroundExecutor; this.pongCacher = pongCacher; this.simppManager = simppManager; this.updateHandler = updateHandler; this.udpCrawlerPingHandlerFactory = udpCrawlerPingHandlerFactory; this.pingRequestFactory = pingRequestFactory; this.messageHandlerBinder = messageHandlerBinder; this.multicastGuidMap = guidMapManager.getMap(); this.udpReplyHandlerCache = udpReplyHandlerCache; this.inspectionRequestHandlerFactory = inspectionRequestHandlerFactory; this.oobHandlerFactory = oobHandlerFactory; this.MACCalculatorRepositoryManager = MACCalculatorRepositoryManager; _clientGUID = applicationServices.getMyGUID(); _bypassedResultsCache = new BypassedResultsCache(activityCallback, downloadManager); } /** Sets a new handler to the given handlerMap, for the given class. */ private boolean setHandler(ConcurrentMap<Class<? extends Message>, MessageHandler> handlerMap, Class<? extends Message> clazz, MessageHandler handler) { if (handler != null) { MessageHandler old = handlerMap.put(clazz, handler); if(old != null) { LOG.warn("Ejecting old handler: " + old + " for clazz: " + clazz); } return true; } else { return handlerMap.remove(clazz) != null; } } /** * Adds the given handler to the handlerMap for the given class. * If a handler already existed, this will construct a DualMessageHandler * so that both are handlers are notified. * * @param handlerMap * @param clazz * @param handler */ private void addHandler(ConcurrentMap<Class<? extends Message>, MessageHandler> handlerMap, Class<? extends Message> clazz, MessageHandler handler) { MessageHandler existing = handlerMap.get(clazz); if(existing != null) { // non-blocking addition -- continue trying until we succesfully // replace the prior handler w/ a dual version of that handler while(true) { MessageHandler dual = new DualMessageHandler(handler, existing); if(handlerMap.replace(clazz, existing, dual)) break; existing = handlerMap.get(clazz); dual = new DualMessageHandler(handler, existing); } } else { setHandler(handlerMap, clazz, handler); } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#setMessageHandler(java.lang.Class, com.limegroup.gnutella.messagehandlers.MessageHandler) */ public void setMessageHandler(Class<? extends Message> clazz, MessageHandler handler) { setHandler(messageHandlers, clazz, handler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#addMessageHandler(java.lang.Class, com.limegroup.gnutella.messagehandlers.MessageHandler) */ public void addMessageHandler(Class<? extends Message> clazz, MessageHandler handler) { addHandler(messageHandlers, clazz, handler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getMessageHandler(java.lang.Class) */ public MessageHandler getMessageHandler(Class<? extends Message> clazz) { return messageHandlers.get(clazz); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#setUDPMessageHandler(java.lang.Class, com.limegroup.gnutella.messagehandlers.MessageHandler) */ public void setUDPMessageHandler(Class<? extends Message> clazz, MessageHandler handler) { setHandler(udpMessageHandlers, clazz, handler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#addUDPMessageHandler(java.lang.Class, com.limegroup.gnutella.messagehandlers.MessageHandler) */ public void addUDPMessageHandler(Class<? extends Message> clazz, MessageHandler handler) { addHandler(udpMessageHandlers, clazz, handler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getUDPMessageHandler(java.lang.Class) */ public MessageHandler getUDPMessageHandler(Class<? extends Message> clazz) { return udpMessageHandlers.get(clazz); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#setMulticastMessageHandler(java.lang.Class, com.limegroup.gnutella.messagehandlers.MessageHandler) */ public void setMulticastMessageHandler(Class<? extends Message> clazz, MessageHandler handler) { setHandler(multicastMessageHandlers, clazz, handler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#addMulticastMessageHandler(java.lang.Class, com.limegroup.gnutella.messagehandlers.MessageHandler) */ public void addMulticastMessageHandler(Class<? extends Message> clazz, MessageHandler handler) { addHandler(multicastMessageHandlers, clazz, handler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getMulticastMessageHandler(java.lang.Class) */ public MessageHandler getMulticastMessageHandler(Class<? extends Message> clazz) { return multicastMessageHandlers.get(clazz); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#initialize() */ public void start() { // TODO listener leaking, we should have a shutdown event connectionManager.addEventListener(connectionListener); QRP_PROPAGATOR.start(); // schedule a runner to clear unused out-of-band replies backgroundExecutor.scheduleWithFixedDelay(new Expirer(), CLEAR_TIME, CLEAR_TIME, TimeUnit.MILLISECONDS); // schedule a runner to clear guys we've connected back to backgroundExecutor.scheduleWithFixedDelay(new ConnectBackExpirer(), 10 * CLEAR_TIME, 10 * CLEAR_TIME, TimeUnit.MILLISECONDS); // schedule a runner to send hops-flow messages backgroundExecutor.scheduleWithFixedDelay(new HopsFlowManager(uploadManager, connectionManager), HOPS_FLOW_INTERVAL*10, HOPS_FLOW_INTERVAL, TimeUnit.MILLISECONDS); backgroundExecutor.scheduleWithFixedDelay(new UDPReplyCleaner(), UDP_REPLY_CACHE_TIME, UDP_REPLY_CACHE_TIME, TimeUnit.MILLISECONDS); // runner to clean up OOB sessions OOBHandler oobHandler = oobHandlerFactory.get(); backgroundExecutor.scheduleWithFixedDelay(oobHandler, CLEAR_TIME, CLEAR_TIME, TimeUnit.MILLISECONDS); // handler for inspection requests InspectionRequestHandler inspectionHandler = inspectionRequestHandlerFactory.get(); messageHandlerBinder.bind(this); setMessageHandler(PingRequest.class, new PingRequestHandler()); setMessageHandler(PingReply.class, new PingReplyHandler()); setMessageHandler(QueryRequest.class, new QueryRequestHandler()); setMessageHandler(QueryReply.class, new QueryReplyHandler()); setMessageHandler(ResetTableMessage.class, new ResetTableHandler()); setMessageHandler(PatchTableMessage.class, new PatchTableHandler()); setMessageHandler(TCPConnectBackVendorMessage.class, new TCPConnectBackHandler()); setMessageHandler(UDPConnectBackVendorMessage.class, new UDPConnectBackHandler()); setMessageHandler(TCPConnectBackRedirect.class, new TCPConnectBackRedirectHandler()); setMessageHandler(UDPConnectBackRedirect.class, new UDPConnectBackRedirectHandler()); setMessageHandler(PushProxyRequest.class, new PushProxyRequestHandler()); setMessageHandler(QueryStatusResponse.class, new QueryStatusResponseHandler()); setMessageHandler(HeadPing.class, new HeadPingHandler()); setMessageHandler(SimppRequestVM.class, new SimppRequestVMHandler()); setMessageHandler(SimppVM.class, new SimppVMHandler()); setMessageHandler(UpdateRequest.class, new UpdateRequestHandler()); setMessageHandler(UpdateResponse.class, new UpdateResponseHandler()); setMessageHandler(HeadPong.class, new HeadPongHandler()); setMessageHandler(DHTContactsMessage.class, new DHTContactsMessageHandler()); setMessageHandler(VendorMessage.class, new VendorMessageHandler()); setMessageHandler(InspectionRequest.class, inspectionHandler); setUDPMessageHandler(QueryRequest.class, new UDPQueryRequestHandler()); setUDPMessageHandler(QueryReply.class, new UDPQueryReplyHandler(oobHandler)); setUDPMessageHandler(PingRequest.class, new UDPPingRequestHandler()); setUDPMessageHandler(PingReply.class, new UDPPingReplyHandler()); setUDPMessageHandler(LimeACKVendorMessage.class, new UDPLimeACKVendorMessageHandler()); setUDPMessageHandler(ReplyNumberVendorMessage.class, oobHandler); setUDPMessageHandler(UDPCrawlerPing.class, udpCrawlerPingHandlerFactory.get()); setUDPMessageHandler(HeadPing.class, new UDPHeadPingHandler()); setUDPMessageHandler(UpdateRequest.class, new UDPUpdateRequestHandler()); setUDPMessageHandler(ContentResponse.class, new UDPContentResponseHandler()); setUDPMessageHandler(InspectionRequest.class, inspectionHandler); setMulticastMessageHandler(QueryRequest.class, new MulticastQueryRequestHandler()); //setMulticastMessageHandler(QueryReply.class, new MulticastQueryReplyHandler()); setMulticastMessageHandler(PingRequest.class, new MulticastPingRequestHandler()); //setMulticastMessageHandler(PingReply.class, new MulticastPingReplyHandler()); } public void stop() { connectionManager.removeEventListener(connectionListener); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#originateQueryGUID(byte[]) */ public void originateQueryGUID(byte[] guid) { _queryRouteTable.routeReply(guid, forMeReplyHandler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#queryKilled(com.limegroup.gnutella.GUID) */ public void queryKilled(GUID guid) throws IllegalArgumentException { if (guid == null) throw new IllegalArgumentException("Input GUID is null!"); _bypassedResultsCache.queryKilled(guid); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#downloadFinished(com.limegroup.gnutella.GUID) */ public void downloadFinished(GUID guid) throws IllegalArgumentException { if (guid == null) throw new IllegalArgumentException("Input GUID is null!"); _bypassedResultsCache.downloadFinished(guid); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getQueryLocs(com.limegroup.gnutella.GUID) */ public Set<GUESSEndpoint> getQueryLocs(GUID guid) { return _bypassedResultsCache.getQueryLocs(guid); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getPingRouteTableDump() */ public String getPingRouteTableDump() { return _pingRouteTable.toString(); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getQueryRouteTableDump() */ public String getQueryRouteTableDump() { return _queryRouteTable.toString(); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getPushRouteTableDump() */ public String getPushRouteTableDump() { return _pushRouteTable.toString(); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#removeConnection(com.limegroup.gnutella.ReplyHandler) */ private void removeConnection(ReplyHandler rh) { queryDispatcher.removeReplyHandler(rh); _pingRouteTable.removeReplyHandler(rh); _queryRouteTable.removeReplyHandler(rh); _pushRouteTable.removeReplyHandler(rh); _headPongRouteTable.removeReplyHandler(rh); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#handleMessage(com.limegroup.gnutella.messages.Message, com.limegroup.gnutella.RoutedConnection) */ public void handleMessage(Message msg, ReplyHandler receivingConnection) { // Increment hops and decrease TTL. msg.hop(); MessageHandler msgHandler = getMessageHandler(msg.getHandlerClass()); if (msgHandler != null) { msgHandler.handleMessage(msg, null, receivingConnection); } else if (msg instanceof VendorMessage) { msgHandler = getMessageHandler(VendorMessage.class); if (msgHandler != null) { msgHandler.handleMessage(msg, null, receivingConnection); } } //This may trigger propogation of query route tables. We do this AFTER //any handshake pings. Otherwise we'll think all clients are old //clients. //forwardQueryRouteTables(); notifyMessageListener(msg, receivingConnection); } /** * Notifies any message listeners of this message's guid about the message. * This holds no locks. */ private final void notifyMessageListener(Message msg, ReplyHandler handler) { List<MessageListener> all = _messageListeners.get(msg.getGUID()); if(all != null) { for(MessageListener next : all) { next.processMessage(msg, handler); } } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#handleUDPMessage(com.limegroup.gnutella.messages.Message, java.net.InetSocketAddress) */ public void handleUDPMessage(Message msg, InetSocketAddress addr) { // Increment hops and decrement TTL. msg.hop(); if(msg instanceof QueryReply) { // check to see if it was from the multicast map. byte[] origGUID = multicastGuidMap.getOriginalGUID(msg.getGUID()); if(origGUID != null) { msg = queryReplyFactory.createQueryReply(origGUID, (QueryReply)msg); ((QueryReply)msg).setMulticastAllowed(true); } } ReplyHandler replyHandler = udpReplyHandlerCache.getUDPReplyHandler(addr); MessageHandler msgHandler = getUDPMessageHandler(msg.getHandlerClass()); if (msgHandler != null) { msgHandler.handleMessage(msg, addr, replyHandler); } else if (msg instanceof VendorMessage) { msgHandler = getUDPMessageHandler(VendorMessage.class); if (msgHandler != null) { msgHandler.handleMessage(msg, addr, replyHandler); } } notifyMessageListener(msg, replyHandler); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#handleMulticastMessage(com.limegroup.gnutella.messages.Message, java.net.InetSocketAddress) */ public void handleMulticastMessage(Message msg, InetSocketAddress addr) { // Use this assert for testing only -- it is a dangerous assert // to have in the field, as not all messages currently set the // network int appropriately. // If someone sends us messages we're not prepared to handle, // this could cause widespreaad AssertFailures // Assert.that(msg.isMulticast(), // "non multicast message in handleMulticastMessage: " + msg); // no multicast messages should ever have been // set with a TTL greater than 1. if (msg.getTTL() > 1) { return; } // Increment hops and decrement TTL. msg.hop(); if (NetworkUtils.isLocalAddress(addr.getAddress()) && !ConnectionSettings.ALLOW_MULTICAST_LOOPBACK.getValue()) { return; } ReplyHandler replyHandler = udpReplyHandlerCache.getUDPReplyHandler(addr); MessageHandler msgHandler = getMulticastMessageHandler(msg.getHandlerClass()); if (msgHandler != null) { msgHandler.handleMessage(msg, addr, replyHandler); } else if (msg instanceof VendorMessage) { msgHandler = getMulticastMessageHandler(VendorMessage.class); if (msgHandler != null) { msgHandler.handleMessage(msg, addr, replyHandler); } } notifyMessageListener(msg, replyHandler); } /** * Returns true if the Query has a valid AddressSecurityToken. false if it isn't present * or valid. */ protected boolean hasValidQueryKey(InetAddress ip, int port, QueryRequest qr) { AddressSecurityToken qk = qr.getQueryKey(); if (qk == null) return false; return qk.isFor(ip, port); } /** * Sends an ack back to the GUESS client node. */ protected void sendAcknowledgement(InetSocketAddress addr, byte[] guid) { Endpoint host = connectionManager.getConnectedGUESSUltrapeer(); PingReply reply; if(host != null) { try { reply = pingReplyFactory.createGUESSReply(guid, (byte)1, host); } catch(UnknownHostException e) { reply = createPingReply(guid); } } else { reply = createPingReply(guid); } // No GUESS endpoints existed and our IP/port was invalid. if( reply == null ) return; udpService.send(reply, addr.getAddress(), addr.getPort()); } /** * Creates a new <tt>PingReply</tt> from the set of cached * GUESS endpoints, or a <tt>PingReply</tt> for localhost * if no GUESS endpoints are available. */ private PingReply createPingReply(byte[] guid) { GUESSEndpoint endpoint = queryUnicaster.getUnicastEndpoint(); if(endpoint == null) { if(networkManager.isIpPortValid()) return pingReplyFactory.create(guid, (byte)1); else return null; } else { return pingReplyFactory.createGUESSReply(guid, (byte)1, endpoint.getPort(), endpoint.getInetAddress().getAddress()); } } /** * The handler for PingRequests received in * RoutedConnection.loopForMessages(). Checks the routing table to see * if the request has already been seen. If not, calls handlePingRequest. */ final void handlePingRequestPossibleDuplicate( PingRequest request, ReplyHandler handler) { if(_pingRouteTable.tryToRouteReply(request.getGUID(), handler) != null) handlePingRequest(request, handler); } /** * The handler for PingRequests received in * RoutedConnection.loopForMessages(). Checks the routing table to see * if the request has already been seen. If not, calls handlePingRequest. */ final void handleUDPPingRequestPossibleDuplicate( PingRequest request, ReplyHandler handler, InetSocketAddress addr) { if(_pingRouteTable.tryToRouteReply(request.getGUID(), handler) != null) handleUDPPingRequest(request, handler, addr); } /** * The handler for QueryRequests received in * RoutedConnection.loopForMessages(). Checks the routing table to see * if the request has already been seen. If not, calls handleQueryRequest. */ final void handleQueryRequestPossibleDuplicate( QueryRequest request, ReplyHandler receivingConnection) { // With the new handling of probe queries (TTL 1, Hops 0), we have a few // new options: // 1) If we have a probe query.... // a) If you have never seen it before, put it in the route table and // set the ttl appropriately // b) If you have seen it before, then just count it as a duplicate // 2) If it isn't a probe query.... // a) Is it an extension of a probe? If so re-adjust the TTL. // b) Is it a 'normal' query (no probe extension or already extended)? // Then check if it is a duplicate: // 1) If it a duplicate, just count it as one // 2) If it isn't, put it in the route table but no need to setTTL // we msg.hop() before we get here.... // hops may be 1 or 2 because we may be probing with a leaf query.... final boolean isProbeQuery = ((request.getTTL() == 0) && ((request.getHops() == 1) || (request.getHops() == 2))); ResultCounter counter = _queryRouteTable.tryToRouteReply(request.getGUID(), receivingConnection); if(counter != null) { // query is new (probe or normal) // 1a: set the TTL of the query so it can be potentially extended if (isProbeQuery) _queryRouteTable.setTTL(counter, (byte)1); // 1a and 2b2 // if a new probe or a new request, do everything (so input true // below) handleQueryRequest(request, receivingConnection, counter, true); } else if (!isProbeQuery) {// probe extension? if (wasProbeQuery(request)) { // rebroadcast out but don't locally evaluate.... handleQueryRequest(request, receivingConnection, counter, false); } else { // 2b1: not a correct extension, so call it a duplicate.... tallyDupQuery(request); } } else if (isProbeQuery) { // 1b: duplicate probe tallyDupQuery(request); } } private boolean wasProbeQuery(QueryRequest request) { // if the current TTL is large enough and the old TTL was 1, then this // was a probe query.... // NOTE: that i'm setting the ttl to be the actual ttl of the query. i // could set it to some max value, but since we only allow TTL 1 queries // to be extended, it isn't a big deal what i set it to. in fact, i'm // setting the ttl to the correct value if we had full expanding rings // of queries. return ((request.getTTL() > 0) && _queryRouteTable.getAndSetTTL(request.getGUID(), (byte)1, (byte)(request.getTTL()+1))); } private void tallyDupQuery(QueryRequest request) { duplicateQueryCounter.duplicateQuery(request); } /** * Special handler for UDP queries. Checks the routing table to see if * the request has already been seen, handling it if not. * * @param query the UDP <tt>QueryRequest</tt> * @param handler the <tt>ReplyHandler</tt> that will handle the reply * @return false if it was a duplicate, true if it was not. */ final boolean handleUDPQueryRequestPossibleDuplicate(QueryRequest request, ReplyHandler handler) { ResultCounter counter = _queryRouteTable.tryToRouteReply(request.getGUID(), handler); if(counter != null) { handleQueryRequest(request, handler, counter, true); return true; } return false; } /** * Handles pings from the network. With the addition of pong caching, this * method will either respond with cached pongs, or it will ignore the ping * entirely if another ping has been received from this connection very * recently. If the ping is TTL=1, we will always process it, as it may * be a hearbeat ping to make sure the connection is alive and well. * * @param ping the ping to handle * @param handler the <tt>ReplyHandler</tt> instance that sent the ping */ final private void handlePingRequest(PingRequest ping, ReplyHandler handler) { // Send it along if it's a heartbeat ping or if we should allow new // pings on this connection. if(ping.isHeartbeat() || handler.allowNewPings()) { respondToPingRequest(ping, handler); } } /** * The default handler for PingRequests received in * RoutedConnection.loopForMessages(). This implementation updates stats, * does the broadcast, and generates a response. * * You can customize behavior in three ways: * 1. Override. You can assume that duplicate messages * (messages with the same GUID that arrived via different paths) have * already been filtered. If you want stats updated, you'll * have to call super.handlePingRequest. * 2. Override broadcastPingRequest. This allows you to use the default * handling framework and just customize request routing. * 3. Implement respondToPingRequest. This allows you to use the default * handling framework and just customize responses. */ protected void handleUDPPingRequest(PingRequest pingRequest, ReplyHandler handler, InetSocketAddress addr) { if (pingRequest.isQueryKeyRequest()) sendQueryKeyPong(pingRequest, addr); else respondToUDPPingRequest(pingRequest, addr, handler); } /** * Generates a AddressSecurityToken for the source (described by addr) and sends the * AddressSecurityToken to it via a AddressSecurityToken pong.... */ protected void sendQueryKeyPong(PingRequest pr, InetSocketAddress addr) { // check if we're getting bombarded long now = System.currentTimeMillis(); if (now - _lastQueryKeyTime < SearchSettings.QUERY_KEY_DELAY.getValue()) return; _lastQueryKeyTime = now; // after find more sources and OOB queries, everyone can dole out query // keys.... // generate a AddressSecurityToken (quite quick - current impl. (DES) is super // fast! InetAddress address = addr.getAddress(); int port = addr.getPort(); AddressSecurityToken key = new AddressSecurityToken(address, port, MACCalculatorRepositoryManager.get()); // respond with Pong with QK, as GUESS requires.... PingReply reply = pingReplyFactory.createQueryKeyReply(pr.getGUID(), (byte)1, key); udpService.send(reply, addr.getAddress(), addr.getPort()); } protected void handleUDPPingReply(PingReply reply, ReplyHandler handler, InetAddress address, int port) { if (reply.getQueryKey() != null) { // this is a PingReply in reply to my AddressSecurityToken Request - //consume the Pong and return, don't process as usual.... onDemandUnicaster.handleQueryKeyPong(reply); return; } // do not process the pong if different from the host // described in the reply if((reply.getPort() != port) || (!reply.getInetAddress().equals(address))) { return; } // normal pong processing... handlePingReply(reply, handler); } /** * The default handler for QueryRequests received in * RoutedConnection.loopForMessages(). This implementation updates stats, * does the broadcast, and generates a response. * * You can customize behavior in three ways: * 1. Override. You can assume that duplicate messages * (messages with the same GUID that arrived via different paths) have * already been filtered. If you want stats updated, you'll * have to call super.handleQueryRequest. * 2. Override broadcastQueryRequest. This allows you to use the default * handling framework and just customize request routing. * 3. Implement respondToQueryRequest. This allows you to use the default * handling framework and just customize responses. * * @param locallyEvaluate false if you don't want to send the query to * leaves and yourself, true otherwise.... */ protected void handleQueryRequest(QueryRequest request, ReplyHandler handler, ResultCounter counter, boolean locallyEvaluate) { // Apply the personal filter to decide whether the callback // should be informed of the query if (!handler.isPersonalSpam(request)) { activityCallback.get().handleQueryString(request.getQuery()); } // if it's a request from a leaf and we GUESS, send it out via GUESS -- // otherwise, broadcast it if it still has TTL //if(handler.isSupernodeClientConnection() && // RouterService.isGUESSCapable()) //unicastQueryRequest(request, handler); //else if(request.getTTL() > 0) { updateMessage(request, handler); if(handler.isSupernodeClientConnection() && counter != null) { if (request.desiresOutOfBandReplies()) { // this query came from a leaf - so check if it desires OOB // responses and make sure that the IP it advertises is legit - // if it isn't drop away.... // no need to check the port - if you are attacking yourself you // got problems String remoteAddr = handler.getInetAddress().getHostAddress(); String myAddress = NetworkUtils.ip2string(networkManager.getAddress()); if (request.getReplyAddress().equals(remoteAddr)) ; // continue below, everything looks good else if (request.getReplyAddress().equals(myAddress) && networkManager.isOOBCapable()) // i am proxying - maybe i should check my success rate but // whatever... ; else return; } // don't send it to leaves here -- the dynamic querier will // handle that locallyEvaluate = false; // do respond with files that we may have, though respondToQueryRequest(request, _clientGUID, handler); multicastQueryRequest(request); if(handler.isGoodLeaf()) { sendDynamicQuery(queryHandlerFactory.createHandlerForNewLeaf(request, handler, counter), handler); } else { sendDynamicQuery(queryHandlerFactory.createHandlerForOldLeaf(request, handler, counter), handler); } } else if(request.getTTL() > 0 && connectionServices.isSupernode()) { // send the request to intra-Ultrapeer connections -- this does // not send the request to leaves if(handler.isGoodUltrapeer()) { // send it to everyone forwardQueryToUltrapeers(request, handler); } else { // otherwise, only send it to some connections forwardLimitedQueryToUltrapeers(request, handler); } } if (locallyEvaluate) { // always forward any queries to leaves -- this only does // anything when this node's an Ultrapeer forwardQueryRequestToLeaves(request, handler); // if (I'm firewalled AND the source is firewalled) AND // NOT(he can do a FW transfer and so can i) then don't reply... if ((request.isFirewalledSource() && !networkManager.acceptedIncomingConnection()) && !(request.canDoFirewalledTransfer() && networkManager.canDoFWT()) ) return; respondToQueryRequest(request, _clientGUID, handler); } } /** Handles a ACK message - looks up the QueryReply and sends it out of * band. */ protected void handleLimeACKMessage(LimeACKVendorMessage ack, InetSocketAddress addr) { GUID.TimedGUID refGUID = new GUID.TimedGUID(new GUID(ack.getGUID()), TIMED_GUID_LIFETIME); QueryResponseBundle bundle = _outOfBandReplies.remove(refGUID); // token is null for old oob messages, it will just be ignored then SecurityToken securityToken = ack.getSecurityToken(); if ((bundle != null) && (ack.getNumResults() > 0)) { InetAddress iaddr = addr.getAddress(); int port = addr.getPort(); //convert responses to QueryReplies, but only send as many as the //node wants Iterable<QueryReply> iterable; if (ack.getNumResults() < bundle._responses.length) { // TODO move selection to responseToQueryReplies methods for randomization Response[] desired = new Response[ack.getNumResults()]; System.arraycopy(bundle._responses, 0, desired, 0, desired.length); iterable = responsesToQueryReplies(desired, bundle._query, 1, securityToken); } else { iterable = responsesToQueryReplies(bundle._responses, bundle._query, 1, securityToken); } //send the query replies for(QueryReply queryReply : iterable) udpService.send(queryReply, iaddr, port); } // else some sort of routing error or attack? // TODO: tally some stat stuff here } /** * Adds the sender of the reply message to an internal cache under the guid of the message * if the sender can receive unsolicited UDP. * * @see {@link #getQueryLocs(GUID)} for retrieval */ public boolean addBypassedSource(ReplyNumberVendorMessage reply, ReplyHandler handler) { //if the reply cannot receive unsolicited udp, there is no point storing it if (!reply.canReceiveUnsolicited()) { return false; } GUESSEndpoint ep = new GUESSEndpoint(handler.getInetAddress(), handler.getPort()); return _bypassedResultsCache.addBypassedSource(new GUID(reply.getGUID()), ep); } /** * Adds the sender of the reply message to an internal cache under the guid of the message * if the sender can receive unsolicited UDP. * * @see {@link #getQueryLocs(GUID)} for retrieval */ public boolean addBypassedSource(QueryReply reply, ReplyHandler handler) { try { if (reply.getHostData().isFirewalled()) return false; } catch (BadPacketException bpe){ return false; } GUESSEndpoint ep = new GUESSEndpoint(handler.getInetAddress(), handler.getPort()); return _bypassedResultsCache.addBypassedSource(new GUID(reply.getGUID()), ep); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getNumOOBToRequest(com.limegroup.gnutella.messages.vendor.ReplyNumberVendorMessage) */ public int getNumOOBToRequest(ReplyNumberVendorMessage reply) { GUID qGUID = new GUID(reply.getGUID()); int numResults = searchResultHandler.getNumResultsForQuery(qGUID); if (numResults < 0) // this may be a proxy query numResults = queryDispatcher.getLeafResultsForQuery(qGUID); if (numResults < 0 || numResults > QueryHandler.ULTRAPEER_RESULTS) { return -1; } return reply.getNumResults(); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#isQueryAlive(com.limegroup.gnutella.GUID) */ public boolean isQueryAlive(GUID guid) { return _queryRouteTable.getReplyHandler(guid.bytes()) != null; } /** Stores (for a limited time) the resps for later out-of-band delivery - * interacts with handleLimeACKMessage * @return true if the operation failed, false if not (i.e. too busy) */ protected boolean bufferResponsesForLaterDelivery(QueryRequest query, Response[] resps) { // store responses by guid for later retrieval synchronized (_outOfBandReplies) { if (_outOfBandReplies.size() < maxBufferedReplies) { GUID.TimedGUID tGUID = new GUID.TimedGUID(new GUID(query.getGUID()), TIMED_GUID_LIFETIME); _outOfBandReplies.put(tGUID, new QueryResponseBundle(query, resps)); return true; } return false; } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#isHostUnicastQueried(com.limegroup.gnutella.GUID, org.limewire.io.IpPort) */ public boolean isHostUnicastQueried(GUID guid, IpPort host) { return onDemandUnicaster.isHostQueriedForGUID(guid, host); } /** * Forwards the UDPConnectBack to neighboring peers * as a UDPConnectBackRedirect request. */ protected void handleUDPConnectBackRequest(UDPConnectBackVendorMessage udp, Connection source) { GUID guidToUse = udp.getConnectBackGUID(); int portToContact = udp.getConnectBackPort(); InetAddress sourceAddr = source.getInetAddress(); Message msg = new UDPConnectBackRedirect(guidToUse, sourceAddr, portToContact); int sentTo = 0; List<RoutedConnection> peers = new ArrayList<RoutedConnection>(connectionManager.getInitializedConnections()); Collections.shuffle(peers); for(RoutedConnection currMC : peers) { if(sentTo >= MAX_UDP_CONNECTBACK_FORWARDS) break; if(currMC == source) continue; if (currMC.getConnectionCapabilities().remoteHostSupportsUDPRedirect() >= 0) { currMC.send(msg); sentTo++; } } } /** * Sends a ping to the person requesting the connectback request. */ protected void handleUDPConnectBackRedirect(UDPConnectBackRedirect udp, Connection source) { // only allow other UPs to send you this message.... if (!source.getConnectionCapabilities().isSupernodeSupernodeConnection()) return; GUID guidToUse = udp.getConnectBackGUID(); int portToContact = udp.getConnectBackPort(); InetAddress addrToContact = udp.getConnectBackAddress(); // only connect back if you aren't connected to the host - that is the // whole point of redirect after all.... Endpoint endPoint = new Endpoint(addrToContact.getAddress(), portToContact); if (connectionManager.isConnectedTo(endPoint.getAddress())) return; // keep track of who you tried connecting back too, don't do it too // much.... String addrString = addrToContact.getHostAddress(); if (!shouldServiceRedirect(_udpConnectBacks,addrString)) return; // mutating twice restores the original guid UDPService.mutateGUID(guidToUse.bytes(), addrToContact, portToContact); PingRequest pr = pingRequestFactory.createPingRequest(guidToUse.bytes(), (byte) 1, (byte) 0); udpService.send(pr, addrToContact, portToContact); } /** * @param map the map that keeps track of recent redirects * @param key the key which we would (have) store(d) in the map * @return whether we should service the redirect request * @modifies the map */ private boolean shouldServiceRedirect(FixedsizeHashMap<String, String> map, String key) { synchronized(map) { String placeHolder = map.get(key); if (placeHolder == null) { try { map.put(key, key); return true; } catch (NoMoreStorageException nomo) { return false; // we've done too many connect backs, stop.... } } else return false; // we've connected back to this guy recently.... } } /** * Forwards the request to neighboring Ultrapeers as a * TCPConnectBackRedirect message. */ protected void handleTCPConnectBackRequest(TCPConnectBackVendorMessage tcp, Connection source) { final int portToContact = tcp.getConnectBackPort(); InetAddress sourceAddr = source.getInetAddress(); Message msg = new TCPConnectBackRedirect(sourceAddr, portToContact); int sentTo = 0; List<RoutedConnection> peers = new ArrayList<RoutedConnection>(connectionManager.getInitializedConnections()); Collections.shuffle(peers); for(RoutedConnection currMC : peers) { if(sentTo >= MAX_TCP_CONNECTBACK_FORWARDS) break; if(currMC == source) continue; if (currMC.getConnectionCapabilities().remoteHostSupportsTCPRedirect() >= 0) { currMC.send(msg); sentTo++; } } } /** * Basically, just get the correct parameters, create a Socket, and * send a "/n/n". */ protected void handleTCPConnectBackRedirect(TCPConnectBackRedirect tcp, Connection source) { // only allow other UPs to send you this message.... if (!source.getConnectionCapabilities().isSupernodeSupernodeConnection()) return; final int portToContact = tcp.getConnectBackPort(); final String addrToContact =tcp.getConnectBackAddress().getHostAddress(); // only connect back if you aren't connected to the host - that is the // whole point of redirect after all.... Endpoint endPoint = new Endpoint(addrToContact, portToContact); if (connectionManager.isConnectedTo(endPoint.getAddress())) return; // keep track of who you tried connecting back too, don't do it too // much.... if (!shouldServiceRedirect(_tcpConnectBacks,addrToContact)) return; TCP_CONNECT_BACKER.execute(new Runnable() { public void run() { Socket sock = null; try { try { sock = socketsManager.connect(new InetSocketAddress(addrToContact, portToContact), 6000, ConnectType.TLS); sock.setSoTimeout(6000); OutputStream os = sock.getOutputStream(); os.write("CONNECT BACK\r\n\r\n".getBytes()); os.flush(); } catch (IOException noTls) { IOUtils.close(sock); sock = socketsManager.connect(new InetSocketAddress(addrToContact, portToContact), 12000, ConnectType.PLAIN); sock.setSoTimeout(12000); OutputStream os = sock.getOutputStream(); os.write("CONNECT BACK\r\n\r\n".getBytes()); os.flush(); } if(LOG.isTraceEnabled()) LOG.trace("Succesful connectback to: " + addrToContact); try { Thread.sleep(500); // let the other side get it. } catch(InterruptedException ignored) { LOG.warn("Interrupted connectback", ignored); } } catch (IOException ignored) { LOG.warn("IOX during connectback", ignored); } finally { IOUtils.close(sock); } } }); } /** * 1) confirm that the connection is Ultrapeer to Leaf, then send your * listening port in a PushProxyAcknowledgement. * 2) Also cache the client's client GUID. */ protected void handlePushProxyRequest(PushProxyRequest ppReq, RoutedConnection source) { if (source.isSupernodeClientConnection() && networkManager.isIpPortValid()) { String stringAddr = NetworkUtils.ip2string(networkManager.getAddress()); InetAddress addr = null; try { addr = InetAddress.getByName(stringAddr); } catch(UnknownHostException uhe) { ErrorService.error(uhe); // impossible } // 1) PushProxyAcknowledgement ack = new PushProxyAcknowledgement(addr,networkManager.getPort(), ppReq.getClientGUID()); source.send(ack); // 2) _pushRouteTable.routeReply(ppReq.getClientGUID().bytes(), source); source.setPushProxyFor(true); } } /** This method should be invoked when this node receives a * QueryStatusResponse message from the wire. If this node is an * Ultrapeer, we should update the Dynamic Querier about the status of * the leaf's query. */ protected void handleQueryStatus(QueryStatusResponse resp, RoutedConnection leaf) { // message only makes sense if i'm a UP and the sender is a leaf if (!leaf.isSupernodeClientConnection()) return; GUID queryGUID = resp.getQueryGUID(); int numResults = resp.getNumResults(); // get the QueryHandler and update the stats.... queryDispatcher.updateLeafResultsForQuery(queryGUID, numResults); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#sendPingRequest(com.limegroup.gnutella.messages.PingRequest, com.limegroup.gnutella.RoutedConnection) */ public void sendPingRequest(PingRequest request, RoutedConnection connection) { if(request == null) { throw new NullPointerException("null ping"); } if(connection == null) { throw new NullPointerException("null connection"); } _pingRouteTable.routeReply(request.getGUID(), forMeReplyHandler); connection.send(request); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#sendQueryRequest(com.limegroup.gnutella.messages.QueryRequest, com.limegroup.gnutella.RoutedConnection) */ public void sendQueryRequest(QueryRequest request, RoutedConnection connection) { if(request == null) { throw new NullPointerException("null query"); } if(connection == null) { throw new NullPointerException("null connection"); } _queryRouteTable.routeReply(request.getGUID(), forMeReplyHandler); connection.send(request); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#broadcastPingRequest(com.limegroup.gnutella.messages.PingRequest) */ public void broadcastPingRequest(PingRequest ping) { if(ping == null) { throw new NullPointerException("null ping"); } _pingRouteTable.routeReply(ping.getGUID(), forMeReplyHandler); broadcastPingRequest(ping, forMeReplyHandler, connectionManager); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#sendDynamicQuery(com.limegroup.gnutella.messages.QueryRequest) */ public void sendDynamicQuery(QueryRequest query) { if(query == null) { throw new NullPointerException("null QueryHandler"); } // get the result counter so we can track the number of results ResultCounter counter = _queryRouteTable.routeReply(query.getGUID(), forMeReplyHandler); if(connectionServices.isSupernode()) { sendDynamicQuery(queryHandlerFactory.createHandlerForMe(query, counter), forMeReplyHandler); } else { originateLeafQuery(query); } // always send the query to your multicast people originateMulticastQuery(query); } /** * Originates a multicast query from this host. * This will alter the GUID of the query and store it in a mapping * of new -> old GUID. When replies come in, if they have the new GUID, * they are reset to be the old one and the multicast flag is allowed. * * @param query * @return the newGUID that the multicast query is using. */ protected void originateMulticastQuery(QueryRequest query) { byte[] newGUID = GUID.makeGuid(); QueryRequest mquery = queryRequestFactory.createMulticastQuery(newGUID, query); multicastGuidMap.addMapping(query.getGUID(), newGUID, MULTICAST_GUID_EXPIRE_TIME); multicastQueryRequest(mquery); } /** * Initiates a dynamic query. Only Ultrapeer should call this method, * as this technique relies on fairly high numbers of connections to * dynamically adjust the TTL based on the number of results received, * the number of remaining connections, etc. * * @param qh the <tt>QueryHandler</tt> instance that generates * queries for this dynamic query * @param handler the <tt>ReplyHandler</tt> for routing replies for * this query * @throws <tt>NullPointerException</tt> if the <tt>ResultCounter</tt> * for the guid cannot be found -- this should never happen, or if any * of the arguments is <tt>null</tt> */ private void sendDynamicQuery(QueryHandler qh, ReplyHandler handler) { if(qh == null) { throw new NullPointerException("null QueryHandler"); } else if(handler == null) { throw new NullPointerException("null ReplyHandler"); } queryDispatcher.addQuery(qh); } /** * Broadcasts the ping request to all initialized connections that * are not the receivingConnection, setting up the routing * to the designated PingReplyHandler. This is called from the default * handlePingRequest and the default broadcastPingRequest(PingRequest) * * If different (smarter) broadcasting functionality is desired, override * as desired. If you do, note that receivingConnection may be null (for * requests originating here). */ private void broadcastPingRequest(PingRequest request, ReplyHandler receivingConnection, ConnectionManager manager) { // Note the use of initializedConnections only. // Note that we have zero allocations here. //Broadcast the ping to other connected nodes (supernodes or older //nodes), but DON'T forward any ping not originating from me //along leaf to ultrapeer connections. List<RoutedConnection> list = manager.getInitializedConnections(); int size = list.size(); boolean randomlyForward = false; if(size > 3) randomlyForward = true; double percentToIgnore; for(int i=0; i<size; i++) { RoutedConnection mc = list.get(i); if(!mc.isStable()) continue; if (receivingConnection == forMeReplyHandler || (mc != receivingConnection && !mc.getConnectionCapabilities().isClientSupernodeConnection())) { if(mc.supportsPongCaching()) { percentToIgnore = 0.70; } else { percentToIgnore = 0.90; } if(randomlyForward && (Math.random() < percentToIgnore)) { continue; } else { mc.send(request); } } } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#forwardQueryRequestToLeaves(com.limegroup.gnutella.messages.QueryRequest, com.limegroup.gnutella.ReplyHandler) */ public final void forwardQueryRequestToLeaves(QueryRequest query, ReplyHandler handler) { if(!connectionServices.isSupernode()) return; //use query routing to route queries to client connections //send queries only to the clients from whom query routing //table has been received List<RoutedConnection> list = connectionManager.getInitializedClientConnections(); List<RoutedConnection> hitConnections = new ArrayList<RoutedConnection>(); for(int i=0; i<list.size(); i++) { RoutedConnection mc = list.get(i); if(mc == handler) continue; if(mc.shouldForwardQuery(query)) { hitConnections.add(mc); } } //forward only to a quarter of the leaves in case the query is //very popular. if(list.size() > 8 && (double)hitConnections.size()/(double)list.size() > .8) { int startIndex = (int) Math.floor( Math.random() * hitConnections.size() * 0.75); hitConnections = hitConnections.subList(startIndex, startIndex+hitConnections.size()/4); } for(int i=0; i<hitConnections.size(); i++) { RoutedConnection mc = hitConnections.get(i); // sendRoutedQueryToHost is not called because // we have already ensured it hits the routing table // by filling up the 'hitsConnection' list. mc.send(query); } } /** * Factored-out method that sends a query to a connection that supports * query routing. The query is only forwarded if there's a hit in the * query routing entries. * * @param query the <tt>QueryRequest</tt> to potentially forward * @param mc the <tt>RoutedConnection</tt> to forward the query to * @param handler the <tt>ReplyHandler</tt> that will be entered into * the routing tables to handle any replies * @return <tt>true</tt> if the query was sent, otherwise <tt>false</tt> */ private boolean sendRoutedQueryToHost(QueryRequest query, RoutedConnection mc, ReplyHandler handler) { if (mc.shouldForwardQuery(query)) { //A new client with routing entry, or one that hasn't started //sending the patch. mc.send(query); return true; } return false; } /** * Adds the QueryRequest to the unicaster module. Not much work done here, * see QueryUnicaster for more details. */ protected void unicastQueryRequest(QueryRequest query, ReplyHandler conn) { // set the TTL on outgoing udp queries to 1 query.setTTL((byte)1); queryUnicaster.addQuery(query, conn); } /** * Send the query to the multicast group. */ protected void multicastQueryRequest(QueryRequest query) { // set the TTL on outgoing udp queries to 1 query.setTTL((byte)1); multicastService.send(query); } /** * Broadcasts the query request to all initialized connections that * are not the receivingConnection, setting up the routing * to the designated QueryReplyHandler. This is called from teh default * handleQueryRequest and the default broadcastQueryRequest(QueryRequest) * * If different (smarter) broadcasting functionality is desired, override * as desired. If you do, note that receivingConnection may be null (for * requests originating here). */ // default access for testing void forwardQueryToUltrapeers(QueryRequest query, ReplyHandler handler) { // Note the use of initializedConnections only. // Note that we have zero allocations here. //Broadcast the query to other connected nodes (ultrapeers or older //nodes), but DON'T forward any queries not originating from me //along leaf to ultrapeer connections. List<RoutedConnection> list = connectionManager.getInitializedConnections(); int limit = list.size(); for(int i=0; i<limit; i++) { RoutedConnection mc = list.get(i); forwardQueryToUltrapeer(query, handler, mc); } } /** * Performs a limited broadcast of the specified query. This is * useful, for example, when receiving queries from old-style * connections that we don't want to forward to all connected * Ultrapeers because we don't want to overly magnify the query. * * @param query the <tt>QueryRequest</tt> instance to forward * @param handler the <tt>ReplyHandler</tt> from which we received * the query */ // default access for testing void forwardLimitedQueryToUltrapeers(QueryRequest query, ReplyHandler handler) { //Broadcast the query to other connected nodes (ultrapeers or older //nodes), but DON'T forward any queries not originating from me //along leaf to ultrapeer connections. List<RoutedConnection> list = connectionManager.getInitializedConnections(); int limit = list.size(); int connectionsNeededForOld = OLD_CONNECTIONS_TO_USE; for(int i=0; i<limit; i++) { // if we've already queried enough old connections for // an old-style query, break out if(connectionsNeededForOld == 0) break; RoutedConnection mc = list.get(i); // if the query is comiing from an old connection, try to // send it's traffic to old connections. Only send it to // new connections if we only have a minimum number left if(mc.isGoodUltrapeer() && (limit-i) > connectionsNeededForOld) { continue; } forwardQueryToUltrapeer(query, handler, mc); // decrement the connections to use connectionsNeededForOld--; } } /** * Forwards the specified query to the specified Ultrapeer. This * encapsulates all necessary logic for forwarding queries to * Ultrapeers, for example handling last hop Ultrapeers specially * when the receiving Ultrapeer supports Ultrapeer query routing, * meaning that we check it's routing tables for a match before sending * the query. * * @param query the <tt>QueryRequest</tt> to forward * @param handler the <tt>ReplyHandler</tt> that sent the query * @param ultrapeer the Ultrapeer to send the query to */ // default access for testing void forwardQueryToUltrapeer(QueryRequest query, ReplyHandler handler, RoutedConnection ultrapeer) { // don't send a query back to the guy who sent it if(ultrapeer == handler) return; // make double-sure we don't send a query received // by a leaf to other Ultrapeers if(ultrapeer.getConnectionCapabilities().isClientSupernodeConnection()) return; // make sure that the ultrapeer understands feature queries. if(query.isFeatureQuery() && !ultrapeer.getConnectionCapabilities().getRemoteHostSupportsFeatureQueries()) return; // is this the last hop for the query?? boolean lastHop = query.getTTL() == 1; // if it's the last hop to an Ultrapeer that sends // query route tables, route it. if(lastHop && ultrapeer.isUltrapeerQueryRoutingConnection()) { sendRoutedQueryToHost(query, ultrapeer, handler); } else { // otherwise, just send it out ultrapeer.send(query); } } /** * Originate a new query from this leaf node. * * @param qr the <tt>QueryRequest</tt> to send */ // default access for testing void originateLeafQuery(QueryRequest qr) { List<RoutedConnection> list = connectionManager.getInitializedConnections(); // only send to at most 4 Ultrapeers, as we could have more // as a result of race conditions - also, don't send what is new // requests down too many connections final int max = qr.isWhatIsNewRequest() ? 2 : 3; int start = !qr.isWhatIsNewRequest() ? 0 : (int) (Math.floor(Math.random()*(list.size()-1))); int limit = Math.min(max, list.size()); final boolean wantsOOB = qr.desiresOutOfBandReplies(); for(int i=start; i<start+limit; i++) { RoutedConnection mc = list.get(i); QueryRequest qrToSend = qr; if (wantsOOB && (mc.getConnectionCapabilities().remoteHostSupportsLeafGuidance() < 0)) qrToSend = queryRequestFactory.unmarkOOBQuery(qr); mc.originateQuery(qrToSend); } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#originateQuery(com.limegroup.gnutella.messages.QueryRequest, com.limegroup.gnutella.RoutedConnection) */ public boolean originateQuery(QueryRequest query, RoutedConnection mc) { if( query == null ) throw new NullPointerException("null query"); if( mc == null ) throw new NullPointerException("null connection"); // if this is a feature query & the other side doesn't // support it, then don't send it // This is an optimization of network traffic, and doesn't // necessarily need to exist. We could be shooting ourselves // in the foot by not sending this, rendering Feature Searches // inoperable for some users connected to bad Ultrapeers. if(query.isFeatureQuery() && !mc.getConnectionCapabilities().getRemoteHostSupportsFeatureQueries()) return false; mc.originateQuery(query); return true; } /** * Respond to the ping request. Implementations typically will either * do nothing (if they don't think a response is appropriate) or call * sendPingReply(PingReply). * This method is called from the default handlePingRequest. */ protected abstract void respondToPingRequest(PingRequest request, ReplyHandler handler); /** * Responds to a ping received over UDP -- implementations * handle this differently from pings received over TCP, as it is * assumed that the requester only wants pongs from other nodes * that also support UDP messaging. * * @param request the <tt>PingRequest</tt> to service * @param addr the <tt>InetSocketAddress</tt> containing the ping * @param handler the <tt>ReplyHandler</tt> instance from which the * ping was received and to which pongs should be sent */ protected abstract void respondToUDPPingRequest(PingRequest request, InetSocketAddress addr, ReplyHandler handler); /** * Respond to the query request. Implementations typically will either * do nothing (if they don't think a response is appropriate) or call * sendQueryReply(QueryReply). * This method is called from the default handleQueryRequest. */ protected abstract boolean respondToQueryRequest(QueryRequest queryRequest, byte[] clientGUID, ReplyHandler handler); /** * The default handler for PingRequests received in * RoutedConnection.loopForMessages(). This implementation * uses the ping route table to route a ping reply. If an appropriate route * doesn't exist, records the error statistics. On sucessful routing, * the PingReply count is incremented.<p> * * In all cases, the ping reply is recorded into the host catcher.<p> * * Override as desired, but you probably want to call super.handlePingReply * if you do. */ protected void handlePingReply(PingReply reply, ReplyHandler handler) { //update hostcatcher (even if the reply isn't for me) boolean newAddress = hostCatcher.add(reply); if(newAddress && !reply.isUDPHostCache()) { pongCacher.get().addPong(reply); } //First route to originator in usual manner. ReplyHandler replyHandler = _pingRouteTable.getReplyHandler(reply.getGUID()); if (replyHandler != null) { replyHandler.handlePingReply(reply, handler); } else { handler.countDroppedMessage(); } boolean supportsUnicast = reply.supportsUnicast(); //Then, if a marked pong from an Ultrapeer that we've never seen before, //send to all leaf connections except replyHandler (which may be null), //irregardless of GUID. The leafs will add the address then drop the //pong as they have no routing entry. Note that if Ultrapeers are very //prevalent, this may consume too much bandwidth. //Also forward any GUESS pongs to all leaves. if (newAddress && (reply.isUltrapeer() || supportsUnicast)) { List<RoutedConnection> list=connectionManager.getInitializedClientConnections(); for (int i=0; i<list.size(); i++) { RoutedConnection c = list.get(i); assert c != null : "null c."; if (c!=handler && c!=replyHandler && c.allowNewPongs()) { c.handlePingReply(reply, handler); } } } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#handleQueryReply(com.limegroup.gnutella.messages.QueryReply, com.limegroup.gnutella.ReplyHandler) */ public void handleQueryReply(QueryReply queryReply, ReplyHandler handler) { if(queryReply == null) { throw new NullPointerException("null query reply"); } if(handler == null) { throw new NullPointerException("null ReplyHandler"); } // check the altloc count if (!altCountOk(queryReply)) return; //For flow control reasons, we keep track of the bytes routed for this //GUID. Replies with less volume have higher priorities (i.e., lower //numbers). RouteTable.ReplyRoutePair rrp = _queryRouteTable.getReplyHandler(queryReply.getGUID(), queryReply.getTotalLength(), queryReply.getUniqueResultCount(), queryReply.getPartialResultCount(), ByteOrder.beb2int(queryReply.getIPBytes(), 0)); if(rrp != null) { queryReply.setPriority(rrp.getBytesRouted()); // Prepare a routing for a PushRequest, which works // here like a QueryReplyReply // Note the use of getClientGUID() here, not getGUID() _pushRouteTable.routeReply(queryReply.getClientGUID(), handler); //Simple flow control: don't route this message along other //connections if we've already routed too many replies for this //GUID. Note that replies destined for me all always delivered to //the GUI. ReplyHandler rh = rrp.getReplyHandler(); // remember more stats _queryRouteTable.countHopsTTLNet(queryReply); // if this reply is for us, remember even more stats if (rh == forMeReplyHandler) _queryRouteTable.timeStampResults(queryReply); if(!shouldDropReply(rrp, rh, queryReply)) { rh.handleQueryReply(queryReply, handler); // also add to the QueryUnicaster for accounting - basically, // most results will not be relevant, but since it is a simple // HashSet lookup, it isn't a prohibitive expense... queryUnicaster.handleQueryReply(queryReply); } else { handler.countDroppedMessage(); } } else { handler.countDroppedMessage(); } } private boolean altCountOk(QueryReply qr) { try { for (Response r : qr.getResultsAsList()) { if (r.getLocations().size() > FilterSettings.MAX_ALTS_PER_RESPONSE.getValue()) return false; } } catch (BadPacketException bpe) { // may get routed to someone who can parse it } return true; } /** * Checks if the <tt>QueryReply</tt> should be dropped for various reasons. * * Reason 1) The reply has already routed enough traffic. Based on per-TTL * hard limits for the number of bytes routed for the given reply guid. * This algorithm favors replies that don't have as far to go on the * network -- i.e., low TTL hits have more liberal limits than high TTL * hits. This ensures that hits that are closer to the query originator * -- hits for which we've already done most of the work, are not * dropped unless we've routed a really large number of bytes for that * guid. This method also checks that hard number of results that have * been sent for this GUID. If this number is greater than a specified * limit, we simply drop the reply. * * Reason 2) The reply was meant for me -- DO NOT DROP. * * Reason 3) The TTL is 0, drop. * * @param rrp the <tt>ReplyRoutePair</tt> containing data about what's * been routed for this GUID * @param ttl the time to live of the query hit * @return <tt>true if the reply should be dropped, otherwise <tt>false</tt> */ private boolean shouldDropReply(RouteTable.ReplyRoutePair rrp, ReplyHandler rh, QueryReply qr) { byte ttl = qr.getTTL(); // Reason 2 -- The reply is meant for me, do not drop it. if( rh == forMeReplyHandler ) return false; // Reason 3 -- drop if TTL is 0. if( ttl == 0 ) { droppedReplyCounter.dropTTL0(); return true; } // Reason 1 ... int resultsRouted = rrp.getResultsRouted(); // drop the reply if we've already sent more than the specified number // of results for this GUID if(resultsRouted > 100) { droppedReplyCounter.tooManyResults((short)resultsRouted); return true; } int bytesRouted = rrp.getBytesRouted(); // send replies with ttl above 2 if we've routed under 50K if(ttl > 2 && bytesRouted < 50 * 1024) return false; // send replies with ttl 1 if we've routed under 1000K if(ttl == 1 && bytesRouted < 200 * 1024) return false; // send replies with ttl 2 if we've routed under 333K if(ttl == 2 && bytesRouted < 100 * 1024) return false; droppedReplyCounter.ttlByteDrop(ttl, bytesRouted); // if none of the above conditions holds true, drop the reply return true; } /** * If we get and SimppRequest, get the payload we need from the * SimppManager and send the simpp bytes the the requestor in a SimppVM. */ private void handleSimppRequest(final SimppRequestVM simppReq, final ReplyHandler handler ) { if(simppReq.getVersion() > SimppRequestVM.VERSION) return; //we are not going to deal with these types of requests. byte[] simppBytes = simppManager.get().getSimppBytes(); if(simppBytes != null && simppBytes.length > 0 ) { SimppVM simppVM = new SimppVM(simppBytes); try { handler.handleSimppVM(simppVM); } catch(IOException iox) {//uanble to send the SimppVM. Nothing I can do return; } } } /** * Passes on the SimppVM to the SimppManager which will verify it and * make sure we it's newer than the one we know about, and then make changes * to the settings as necessary, and cause new CapabilityVMs to be sent down * all connections. */ private void handleSimppVM(SimppVM simppVM, ReplyHandler handler) { simppManager.get().checkAndUpdate(handler, simppVM.getPayload()); } /** * Handles an update request by sending a response. */ private void handleUpdateRequest(UpdateRequest req, ReplyHandler handler ) { byte[] data = updateHandler.get().getLatestBytes(); if(data != null) { UpdateResponse msg = UpdateResponse.createUpdateResponse(data,req); handler.reply(msg); } } /** Handles a ContentResponse msg -- passing it to the ContentManager. */ private void handleContentResponse(ContentResponse msg, ReplyHandler handler) { contentManager.handleContentResponse(msg); } /** * Passes the request onto the update manager. */ private void handleUpdateResponse(UpdateResponse resp, ReplyHandler handler) { updateHandler.get().handleNewData(resp.getUpdate(), handler); } /** * Returns the appropriate handler from the _pushRouteTable. * This enforces that requests for my clientGUID will return * FOR_ME_REPLY_HANDLER, even if it's not in the table. */ public ReplyHandler getPushHandler(byte[] guid) { ReplyHandler replyHandler = _pushRouteTable.getReplyHandler(guid); if(replyHandler != null) return replyHandler; else if(Arrays.equals(_clientGUID, guid)) return forMeReplyHandler; else return null; } /** * Uses the ping route table to send a PingReply to the appropriate * connection. Since this is used for PingReplies orginating here, no * stats are updated. */ protected void sendPingReply(PingReply pong, ReplyHandler handler) { if(pong == null) { throw new NullPointerException("null pong"); } if(handler == null) { throw new NullPointerException("null reply handler"); } handler.handlePingReply(pong, null); } /** * Uses the query route table to send a QueryReply to the appropriate * connection. Since this is used for QueryReplies orginating here, no * stats are updated. * @throws IOException if no appropriate route exists. */ protected void sendQueryReply(QueryReply queryReply) throws IOException { if(queryReply == null) { throw new NullPointerException("null reply"); } //For flow control reasons, we keep track of the bytes routed for this //GUID. Replies with less volume have higher priorities (i.e., lower //numbers). RouteTable.ReplyRoutePair rrp = _queryRouteTable.getReplyHandler(queryReply.getGUID(), queryReply.getTotalLength(), queryReply.getResultCount(), queryReply.getPartialResultCount(), 0); if(rrp != null) { queryReply.setPriority(rrp.getBytesRouted()); rrp.getReplyHandler().handleQueryReply(queryReply, null); } else throw new IOException("no route for reply"); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#sendPushRequest(com.limegroup.gnutella.messages.PushRequest) */ public void sendPushRequest(PushRequest push) throws IOException { if(push == null) { throw new NullPointerException("null push"); } // Note the use of getClientGUID() here, not getGUID() ReplyHandler replyHandler = getPushHandler(push.getClientGUID()); if(replyHandler != null) replyHandler.handlePushRequest(push, forMeReplyHandler); else throw new IOException("no route for push"); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#sendMulticastPushRequest(com.limegroup.gnutella.messages.PushRequest) */ public void sendMulticastPushRequest(PushRequest push) { if(push == null) { throw new NullPointerException("null push"); } // must have a TTL of 1 assert push.getTTL() == 1 : "multicast push ttl not 1"; multicastService.send(push); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#responsesToQueryReplies(com.limegroup.gnutella.Response[], com.limegroup.gnutella.messages.QueryRequest) */ public Iterable<QueryReply> responsesToQueryReplies(Response[] responses, QueryRequest queryRequest) { return responsesToQueryReplies(responses, queryRequest, 10, null); } /** * Converts the passed responses to QueryReplies. Each QueryReply can * accomodate atmost 255 responses. Not all the responses may get included * in QueryReplies in case the query request came from a far away host. * <p> * NOTE: This method doesnt have any side effect, * and does not modify the state of this object * @param responses The responses to be converted * @param queryRequest The query request corresponding to which we are * generating query replies. * @param REPLY_LIMIT the maximum number of responses to have in each reply. * @param security token might be null * @return Iterable of QueryReply */ // default access for testing Iterable<QueryReply> responsesToQueryReplies(Response[] responses, QueryRequest queryRequest, final int REPLY_LIMIT, SecurityToken securityToken) { //List to store Query Replies List<QueryReply> queryReplies = new LinkedList<QueryReply>(); // get the appropriate queryReply information byte[] guid = queryRequest.getGUID(); byte ttl = (byte)(queryRequest.getHops() + 1); //Return measured speed if possible, or user's speed otherwise. long speed = uploadManager.measuredUploadSpeed(); boolean measuredSpeed=true; if (speed==-1) { speed=ConnectionSettings.CONNECTION_SPEED.getValue(); measuredSpeed=false; } int numResponses = responses.length; int index = 0; int numHops = queryRequest.getHops(); // limit the responses if we're not delivering this // out-of-band and we have a lot of responses if(REPLY_LIMIT > 1 && numHops > 2 && numResponses > HIGH_HOPS_RESPONSE_LIMIT) { int j = (int)(Math.random() * numResponses) % (numResponses - HIGH_HOPS_RESPONSE_LIMIT); Response[] newResponses = new Response[HIGH_HOPS_RESPONSE_LIMIT]; for(int i=0; i<10; i++, j++) { newResponses[i] = responses[j]; } responses = newResponses; numResponses = responses.length; } while (numResponses > 0) { int arraySize; // if there are more than 255 responses, // create an array of 255 to send in the queryReply // otherwise, create an array of whatever size is left. if (numResponses < REPLY_LIMIT) { // break; arraySize = numResponses; } else arraySize = REPLY_LIMIT; Response[] res; // a special case. in the common case where there // are less than 256 responses being sent, there // is no need to copy one array into another. if ( (index == 0) && (arraySize < REPLY_LIMIT) ) { res = responses; } else { res = new Response[arraySize]; // copy the reponses into bite-size chunks for(int i =0; i < arraySize; i++) { res[i] = responses[index]; index++; } } // decrement the number of responses we have left numResponses-= arraySize; // see if there are any open slots // Note: if we are busy, non-metafile results would be filtered. // by this point. boolean busy = !uploadManager.mayBeServiceable(); boolean uploaded = uploadManager.hadSuccesfulUpload(); // We only want to return a "reply to multicast query" QueryReply // if the request travelled a single hop. boolean mcast = queryRequest.isMulticast() && (queryRequest.getTTL() + queryRequest.getHops()) == 1; // We should mark our hits if the remote end can do a firewalled // transfer AND so can we AND we don't accept tcp incoming AND our // external address is valid (needed for input into the reply) final boolean fwTransfer = queryRequest.canDoFirewalledTransfer() && networkManager.canDoFWT() && !networkManager.acceptedIncomingConnection(); if ( mcast ) { ttl = 1; // not strictly necessary, but nice. } List<QueryReply> replies = createQueryReply(guid, ttl, speed, res, _clientGUID, busy, uploaded, measuredSpeed, mcast, fwTransfer, securityToken); //add to the list queryReplies.addAll(replies); }//end of while return queryReplies; } /** * Abstract method for creating query hits. Subclasses must specify * how this list is created. * * @param securityToken might be null, otherwise must be sent in GGEP * of QHD with header "SO" * * @return a <tt>List</tt> of <tt>QueryReply</tt> instances */ protected abstract List<QueryReply> createQueryReply(byte[] guid, byte ttl, long speed, Response[] res, byte[] clientGUID, boolean busy, boolean uploaded, boolean measuredSpeed, boolean isFromMcast, boolean shouldMarkForFWTransfer, SecurityToken securityToken); /** * Handles a message to reset the query route table for the given * connection. * * @param rtm the <tt>ResetTableMessage</tt> for resetting the query * route table * @param mc the <tt>RoutedConnection</tt> for which the query route * table should be reset */ private void handleResetTableMessage(ResetTableMessage rtm, RoutedConnection mc) { // if it's not from a leaf or an Ultrapeer advertising // QRP support, ignore it if(!isQRPConnection(mc)) return; // reset the query route table for this connection synchronized (mc.getQRPLock()) { mc.resetQueryRouteTable(rtm); } // if this is coming from a leaf, make sure we update // our tables so that the dynamic querier has correct // data if(mc.isLeafConnection()) { _lastQueryRouteTable = createRouteTable(); } } /** * Handles a message to patch the query route table for the given * connection. * * @param rtm the <tt>PatchTableMessage</tt> for patching the query * route table * @param mc the <tt>RoutedConnection</tt> for which the query route * table should be patched */ private void handlePatchTableMessage(PatchTableMessage ptm, RoutedConnection mc) { // if it's not from a leaf or an Ultrapeer advertising // QRP support, ignore it if(!isQRPConnection(mc)) return; // patch the query route table for this connection synchronized(mc.getQRPLock()) { mc.patchQueryRouteTable(ptm); } // if this is coming from a leaf, make sure we update // our tables so that the dynamic querier has correct // data if(mc.isLeafConnection()) { _lastQueryRouteTable = createRouteTable(); } } private void updateMessage(QueryRequest request, ReplyHandler handler) { if (SearchSettings.SEND_LIME_RESPONSES.getBoolean() && request.isQueryForLW() && staticMessages.getLimeReply() != null) { QueryReply qr = queryReplyFactory.createQueryReply(request.getGUID(), staticMessages.getLimeReply()); qr.setHops((byte)0); qr.setTTL((byte)(request.getHops()+1)); try { sendQueryReply(qr); } catch (IOException ignored) {} } if (!(handler instanceof Connection)) return; Connection c = (Connection) handler; QueryReply update = staticMessages.getUpdateReply(); if (request.getHops() == 1 && c.getConnectionCapabilities().isOldLimeWire()) { if (update != null) { QueryReply qr = queryReplyFactory.createQueryReply(request.getGUID(), update); try { sendQueryReply(qr); } catch (IOException ignored) { } } } } /** * Utility method for checking whether or not the given connection * is able to pass QRP messages. * * @param c the <tt>Connection</tt> to check * @return <tt>true</tt> if this is a QRP-enabled connection, * otherwise <tt>false</tt> */ private static boolean isQRPConnection(Connection c) { if (c.getConnectionCapabilities().isSupernodeClientConnection()) return true; if (c.getConnectionCapabilities().isUltrapeerQueryRoutingConnection()) return true; return false; } /** Thread the processing of QRP Table delivery. */ private class QRPPropagator extends ManagedThread { public QRPPropagator() { setName("QRPPropagator"); setDaemon(true); } /** While the connection is not closed, sends all data delay. */ public void run() { try { while (true) { // Check for any scheduled QRP table propagations // every 10 seconds Thread.sleep(10*1000); forwardQueryRouteTables(); } } catch(Throwable t) { ErrorService.error(t); } } } /** * Sends updated query routing tables to all connections which haven't * been updated in a while. You can call this method as often as you want; * it takes care of throttling. * @modifies connections */ // default access for testing void forwardQueryRouteTables() { //Check the time to decide if it needs an update. long time = System.currentTimeMillis(); //For all connections to new hosts c needing an update... List<RoutedConnection> list=connectionManager.getInitializedConnections(); QueryRouteTable table = null; List<RouteTableMessage> patches = null; QueryRouteTable lastSent = null; for(int i=0; i<list.size(); i++) { RoutedConnection c = list.get(i); // continue if I'm an Ultrapeer and the node on the // other end doesn't support Ultrapeer-level query // routing if(connectionServices.isSupernode()) { // only skip it if it's not an Ultrapeer query routing // connection if(!c.isUltrapeerQueryRoutingConnection()) { continue; } } // otherwise, I'm a leaf, and don't send routing // tables if it's not a connection to an Ultrapeer // or if query routing is not enabled on the connection else if (!(c.getConnectionCapabilities().isClientSupernodeConnection() && c.getConnectionCapabilities().isQueryRoutingEnabled())) { continue; } // See if it is time for this connections QRP update // This call is safe since only this thread updates time if (time<c.getRoutedConnectionStatistics().getNextQRPForwardTime()) continue; c.getRoutedConnectionStatistics().incrementNextQRPForwardTime(time); // Create a new query route table if we need to if (table == null) { table = createRouteTable(); // Ignores busy leaves _lastQueryRouteTable = table; } //..and send each piece. // Because we tend to send the same list of patches to lots of // Connections, we can reuse the list of RouteTableMessages // between those connections if their last sent // table is exactly the same. // This allows us to only reduce the amount of times we have // to call encode. // (This if works for 'null' sent tables too) if( lastSent == c.getRoutedConnectionStatistics().getQueryRouteTableSent() ) { // if we have not constructed the patches yet, then do so. if( patches == null ) patches = table.encode(lastSent, true); } // If they aren't the same, we have to encode a new set of // patches for this connection. else { lastSent = c.getRoutedConnectionStatistics().getQueryRouteTableSent(); patches = table.encode(lastSent, true); } // If sending QRP tables is turned off, don't send them. if(!ConnectionSettings.SEND_QRP.getValue()) { return; } for(RouteTableMessage next : patches) c.send(next); c.getRoutedConnectionStatistics().setQueryRouteTableSent(table); } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getQueryRouteTable() */ public QueryRouteTable getQueryRouteTable() { return _lastQueryRouteTable; } /** * Creates a query route table appropriate for forwarding to connection c. * This will not include information from c. * @requires queryUpdateLock held */ //default access for testing QueryRouteTable createRouteTable() { QueryRouteTable ret = fileManager.getQRT(); // Add leaves' files if we're an Ultrapeer. if(connectionServices.isSupernode()) { addQueryRoutingEntriesForLeaves(ret); } return ret; } /** * Adds all query routing tables of leaves to the query routing table for * this node for propagation to other Ultrapeers at 1 hop. * * Added "busy leaf" support to prevent a busy leaf from having its QRT * table added to the Ultrapeer's last-hop QRT table. This should reduce * BW costs for UPs with busy leaves. * * @param qrt the <tt>QueryRouteTable</tt> to add to */ private void addQueryRoutingEntriesForLeaves(QueryRouteTable qrt) { List<RoutedConnection> leaves = connectionManager.getInitializedClientConnections(); for(int i=0; i<leaves.size(); i++) { RoutedConnection mc = leaves.get(i); synchronized (mc.getQRPLock()) { // Don't include busy leaves if( !mc.isBusyLeaf() ){ QueryRouteTable qrtr = mc.getRoutedConnectionStatistics().getQueryRouteTableReceived(); if(qrtr != null) { qrt.addAll(qrtr); } } } } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#registerMessageListener(byte[], com.limegroup.gnutella.MessageListener) */ public void registerMessageListener(byte[] guid, MessageListener ml) { ml.registered(guid); synchronized(MESSAGE_LISTENER_LOCK) { Map<byte[], List<MessageListener>> listeners = new TreeMap<byte[], List<MessageListener>>(GUID.GUID_BYTE_COMPARATOR); listeners.putAll(_messageListeners); List<MessageListener> all = listeners.get(guid); if(all == null) { all = new ArrayList<MessageListener>(1); all.add(ml); } else { List<MessageListener> temp = new ArrayList<MessageListener>(all.size() + 1); temp.addAll(all); all = temp; all.add(ml); } listeners.put(guid, Collections.unmodifiableList(all)); _messageListeners = Collections.unmodifiableMap(listeners); } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#unregisterMessageListener(byte[], com.limegroup.gnutella.MessageListener) */ public void unregisterMessageListener(byte[] guid, MessageListener ml) { boolean removed = false; synchronized(MESSAGE_LISTENER_LOCK) { List<MessageListener> all = _messageListeners.get(guid); if(all != null) { all = new ArrayList<MessageListener>(all); if(all.remove(ml)) { removed = true; Map<byte[], List<MessageListener>> listeners = new TreeMap<byte[], List<MessageListener>>(GUID.GUID_BYTE_COMPARATOR); listeners.putAll(_messageListeners); if(all.isEmpty()) listeners.remove(guid); else listeners.put(guid, Collections.unmodifiableList(all)); _messageListeners = Collections.unmodifiableMap(listeners); } } } if(removed) ml.unregistered(guid); } /** * Replies to a head ping sent from the given ReplyHandler. */ private void handleHeadPing(HeadPing ping, ReplyHandler handler) { if (DownloadSettings.DROP_HEADPINGS.getValue()) return; GUID clientGUID = ping.getClientGuid(); ReplyHandler pingee; if(clientGUID != null) pingee = getPushHandler(clientGUID.bytes()); else pingee = forMeReplyHandler; // handle ourselves. //drop the ping if no entry for the given clientGUID if (pingee == null) return; //don't bother routing if this is intended for me. // TODO: Clean up ReplyHandler interface so we aren't // afraid to use it like it's intended. // That way, we can do pingee.handleHeadPing(ping) // and not need this anti-OO instanceof check. if (pingee instanceof ForMeReplyHandler) { // If it's for me, reply directly to the person who sent it. HeadPong pong = headPongFactory.create(ping); handler.reply(pong); // } else { // Otherwise, remember who sent it and forward it on. //remember where to send the pong to. //the pong will have the same GUID as the ping. // Note that this uses the messageGUID, not the clientGUID _headPongRouteTable.routeReply(ping.getGUID(), handler); //and send off the routed ping if ( !(handler instanceof Connection) || ((Connection)handler).getConnectionCapabilities().supportsVMRouting()) pingee.reply(ping); else pingee.reply(new HeadPing(ping)); } } /** * Pure testing method. */ Map<byte[], List<MessageListener>> getMessageListenerMap() { return _messageListeners; } /** * Handles a pong received from the given handler. */ private void handleHeadPong(HeadPong pong, ReplyHandler handler) { ReplyHandler forwardTo = _headPongRouteTable.getReplyHandler(pong.getGUID()); // TODO: Clean up ReplyHandler interface so we're not afraid // to use it correctly. // Ideally, we'd do forwardTo.handleHeadPong(pong) // instead of this instanceof check // if this pong is for me, process it as usual (not implemented yet) if (forwardTo != null && !(forwardTo instanceof ForMeReplyHandler)) { forwardTo.reply(pong); _headPongRouteTable.removeReplyHandler(forwardTo); } } private void handleDHTContactsMessage(DHTContactsMessage msg, ReplyHandler handler) { dhtManager.handleDHTContactsMessage(msg); } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#forwardInspectionRequestToLeaves(com.limegroup.gnutella.messages.vendor.InspectionRequest) */ public void forwardInspectionRequestToLeaves(InspectionRequest ir) { if (!connectionManager.isSupernode()) return; // only inspection requests with return address are forwarded. if (ir.getReturnAddress() == null) return; for (RoutedConnection mc : connectionManager.getInitializedClientConnections()) { if (mc.getConnectionCapabilities().remoteHostSupportsInspections() >= ir.getVersion()) mc.send(ir); } } /** * Should only be used for testing. */ RouteTable getPushRouteTable() { return _pushRouteTable; } RouteTable getHeadPongRouteTable() { return _headPongRouteTable; } /** * Default access for testing only. */ Map<GUID.TimedGUID, QueryResponseBundle> getOutOfBandReplies() { return _outOfBandReplies; } /** * Default access use for testing only. */ void setMaxBufferedReplies(int maxBufferedReplies) { this.maxBufferedReplies = maxBufferedReplies; } private static class QueryResponseBundle { public final QueryRequest _query; public final Response[] _responses; public QueryResponseBundle(QueryRequest query, Response[] responses) { _query = query; _responses = responses; } } /** Expires the UDP-Reply cache. */ private class UDPReplyCleaner implements Runnable { public void run() { messageDispatcher.get().dispatch(new Runnable() { public void run() { udpReplyHandlerCache.clear(); } }); } } /** Can be run to invalidate out-of-band ACKs that we are waiting for.... */ private class Expirer implements Runnable { public void run() { Set<GUID.TimedGUID> toRemove = new HashSet<GUID.TimedGUID>(); synchronized (_outOfBandReplies) { long now = System.currentTimeMillis(); for(GUID.TimedGUID currQB : _outOfBandReplies.keySet()) { if ((currQB != null) && (currQB.shouldExpire(now))) toRemove.add(currQB); } // done iterating through _outOfBandReplies, remove the // keys now... for(GUID.TimedGUID next : toRemove) _outOfBandReplies.remove(next); } } } /** This is run to clear out the registry of connect back attempts... * Made package access for easy test access. */ static class ConnectBackExpirer implements Runnable { public void run() { _tcpConnectBacks.clear(); _udpConnectBacks.clear(); } } private static class HopsFlowManager implements Runnable { private final UploadManager uploadManager; private final ConnectionManager connectionManager; public HopsFlowManager(UploadManager uploadManager, ConnectionManager connectionManager) { this.uploadManager = uploadManager; this.connectionManager = connectionManager; } /* in case we don't want any queries any more */ private static final byte BUSY_HOPS_FLOW = 0; /* in case we want to reenable queries */ private static final byte FREE_HOPS_FLOW = 5; /* small optimization: send only HopsFlowVendorMessages if the busy state changed */ private static boolean _oldBusyState = false; public void run() { // only leafs should use HopsFlow if (connectionManager.isSupernode()) return; // busy hosts don't want to receive any queries, if this node is not // busy, we need to reset the HopsFlow value boolean isBusy = !uploadManager.mayBeServiceable(); // state changed? don't bother the ultrapeer with information // that it already knows. we need to inform new ultrapeers, though. final List<RoutedConnection> connections = connectionManager.getInitializedConnections(); final HopsFlowVendorMessage hops = new HopsFlowVendorMessage(isBusy ? BUSY_HOPS_FLOW : FREE_HOPS_FLOW); if (isBusy == _oldBusyState) { for (int i = 0; i < connections.size(); i++) { RoutedConnection c = connections.get(i); // Yes, we may tell a new ultrapeer twice, but // without a buffer of some kind, we might forget // some ultrapeers. The clean solution would be // to remember the hops-flow value in the connection. if (c != null && c.getConnectionTime() + 1.25 * HOPS_FLOW_INTERVAL > System.currentTimeMillis() && c.getConnectionCapabilities().isClientSupernodeConnection() ) c.send(hops); } } else { _oldBusyState = isBusy; for (int i = 0; i < connections.size(); i++) { RoutedConnection c = connections.get(i); if (c != null && c.getConnectionCapabilities().isClientSupernodeConnection()) c.send(hops); } } } } /* (non-Javadoc) * @see com.limegroup.gnutella.MessageRouter#getOOBExpireTime() */ public long getOOBExpireTime() { return OOB_SESSION_EXPIRE_TIME; } /* * =================================================== * "REGULAR" HANDLER * =================================================== */ private class PingRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handlePingRequestPossibleDuplicate((PingRequest)msg, handler); } } private class PingReplyHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handlePingReply((PingReply)msg, handler); } } private class QueryRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleQueryRequestPossibleDuplicate( (QueryRequest)msg, (RoutedConnection)handler); } } private class QueryReplyHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { // if someone sent a TCP QueryReply with the MCAST header, // that's bad, so ignore it. QueryReply qmsg = (QueryReply)msg; handleQueryReply(qmsg, handler); } } private class ResetTableHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleResetTableMessage((ResetTableMessage)msg, (RoutedConnection)handler); } } private class PatchTableHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handlePatchTableMessage((PatchTableMessage)msg, (RoutedConnection)handler); } } private class TCPConnectBackHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleTCPConnectBackRequest((TCPConnectBackVendorMessage) msg, (RoutedConnection)handler); } } private class UDPConnectBackHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPConnectBackRequest((UDPConnectBackVendorMessage) msg, (RoutedConnection)handler); } } private class TCPConnectBackRedirectHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleTCPConnectBackRedirect((TCPConnectBackRedirect) msg, (RoutedConnection)handler); } } private class UDPConnectBackRedirectHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPConnectBackRedirect((UDPConnectBackRedirect) msg, (RoutedConnection)handler); } } private class PushProxyRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handlePushProxyRequest((PushProxyRequest) msg, (RoutedConnection)handler); } } private class QueryStatusResponseHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleQueryStatus((QueryStatusResponse) msg, (RoutedConnection)handler); } } private class HeadPingHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { //TODO: add the statistics recording code handleHeadPing((HeadPing)msg, handler); } } private class SimppRequestVMHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleSimppRequest((SimppRequestVM)msg, handler); } } private class SimppVMHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleSimppVM((SimppVM)msg, handler); } } private class UpdateRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUpdateRequest((UpdateRequest)msg, handler); } } private class UpdateResponseHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUpdateResponse((UpdateResponse)msg, handler); } } private class HeadPongHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleHeadPong((HeadPong)msg, handler); } } private class DHTContactsMessageHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleDHTContactsMessage((DHTContactsMessage)msg, handler); } } public class VendorMessageHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { Connection c = (Connection)handler; c.handleVendorMessage((VendorMessage)msg); } } /* * =================================================== * UDP HANDLER * =================================================== */ private class UDPQueryRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { InetAddress address = addr.getAddress(); int port = addr.getPort(); // TODO: compare AddressSecurityToken with old generation params. if it matches //send a new one generated with current params if (hasValidQueryKey(address, port, (QueryRequest) msg)) { sendAcknowledgement(addr, msg.getGUID()); // a TTL above zero may indicate a malicious client, as UDP // messages queries should not be sent with TTL above 1. //if(msg.getTTL() > 0) return; handleUDPQueryRequestPossibleDuplicate((QueryRequest)msg, handler); } } } private class UDPPingRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPPingRequestPossibleDuplicate((PingRequest)msg, handler, addr); } } private class UDPPingReplyHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPPingReply((PingReply)msg, handler, addr.getAddress(), addr.getPort()); } } private class UDPLimeACKVendorMessageHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleLimeACKMessage((LimeACKVendorMessage)msg, addr); } } private class UDPHeadPingHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { //TODO: add the statistics recording code handleHeadPing((HeadPing)msg, handler); } } private class UDPUpdateRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUpdateRequest((UpdateRequest)msg, handler); } } private class UDPContentResponseHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleContentResponse((ContentResponse)msg, handler); } } /* * =================================================== * MULTICAST HANDLER * =================================================== */ public class MulticastQueryRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPQueryRequestPossibleDuplicate((QueryRequest) msg, handler); } } public class MulticastQueryReplyHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleQueryReply((QueryReply)msg, handler); } } public class MulticastPingRequestHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPPingRequestPossibleDuplicate((PingRequest)msg, handler, addr); } } public class MulticastPingReplyHandler implements MessageHandler { public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { handleUDPPingReply((PingReply)msg, handler, addr.getAddress(), addr.getPort()); } } /** * This class handles UDP query replies and forwards them to the * {@link OOBHandler} if they are not replies to multicast or unicast * queries. */ public class UDPQueryReplyHandler implements MessageHandler { private final OOBHandler oobHandler; public UDPQueryReplyHandler(OOBHandler oobHandler) { this.oobHandler = oobHandler; } public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { QueryReply reply = (QueryReply)msg; if (reply.isReplyToMulticastQuery() || isHostUnicastQueried(new GUID(reply.getGUID()), handler)) { handleQueryReply(reply, handler); } else { oobHandler.handleMessage(msg, addr, handler); } } } /** * tracks information about messages with a specified guid. */ private class GUIDTracker implements Inspectable, MessageListener, SettingListener { private final List<Map<String,Object>> l = Collections.synchronizedList(new ArrayList<Map<String,Object>>()); private volatile long start; private volatile byte[] lastGuid; public GUIDTracker() { MessageSettings.TRACKING_GUID.addSettingListener(this); } public Object inspect() { Map<String,Object> ret = new HashMap<String,Object>(); ret.put("start",start); if (lastGuid != null) ret.put("guid", Base32.encode(lastGuid)); ret.put("messages",l); return ret; } public void processMessage(Message m, ReplyHandler handler) { Map<String,Object> data = new HashMap<String,Object>(); data.put("ver",1); data.put("time", System.currentTimeMillis()); data.put("source",handler.getInetAddress()+":"+handler.getPort()); data.put("type",m.getClass().getName()); data.put("hops",m.getHops()); data.put("ttl",m.getTTL()); data.put("net",m.getNetwork()); data.put("len",m.getLength()); // why not try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); m.writeQuickly(baos); data.put("pay",baos.toByteArray()); } catch (IOException impossible){} l.add(data); } public void registered(final byte[] guid) { start = System.currentTimeMillis(); lastGuid = guid; // don't keep history for ever. backgroundExecutor.schedule(new Runnable() { public void run () { byte [] last = lastGuid; if (last != null && Arrays.equals(guid,last)) unregisterMessageListener(guid, GUIDTracker.this); } }, 600, TimeUnit.SECONDS); } public void unregistered(byte[] guid) { start = 0; lastGuid = null; l.clear(); // this is the only list manipulation outside of messagedispatch thread } public void settingChanged(SettingEvent evt) { if (evt.getEventType() != SettingEvent.EventType.VALUE_CHANGED || evt.getSetting() != MessageSettings.TRACKING_GUID) return; byte [] last = lastGuid; if (last != null) unregisterMessageListener(last, this); String newGuid = MessageSettings.TRACKING_GUID.getValue(); if (newGuid.length() == 0) return; byte[] guid = Base32.decode(newGuid); registerMessageListener(guid, this); } } /** * Counts reasons why a reply would be dropped. */ private static class DroppedReplyCounter implements Inspectable { // buffer storage is actually inited lazily private final Buffer<Short> tooManyResults = new Buffer<Short>(50); private final Buffer<Byte> ttls = new Buffer<Byte>(50); private final Buffer<Integer> bytesRouted = new Buffer<Integer>(50); private long totalDropped, droppedTTL0; public synchronized Object inspect() { byte [] tooManyResultsByte = new byte[tooManyResults.getSize() * 2]; for (int i = 0; i < tooManyResults.getSize(); i++) ByteOrder.short2beb(tooManyResults.get(i), tooManyResultsByte, i * 2); byte [] ttlsByte = new byte[ttls.getSize()]; for (int i = 0; i < ttls.getSize(); i++) ttlsByte[i] = ttls.get(i); byte [] bytesRoutedByte = new byte[bytesRouted.getSize() * 4]; for (int i = 0; i < bytesRouted.getSize(); i++) ByteOrder.int2beb(bytesRouted.get(i), bytesRoutedByte, i * 4); Map<String,Object> ret = new HashMap<String,Object>(); ret.put("tooMany",tooManyResultsByte); ret.put("ttls",ttlsByte); ret.put("bytes",bytesRoutedByte); ret.put("total",totalDropped); ret.put("ttl0",droppedTTL0); return ret; } public synchronized void tooManyResults(short numResults) { if (!LimeWireUtils.isBetaRelease()) return; totalDropped++; tooManyResults.add(numResults); } public synchronized void ttlByteDrop(byte ttl, int bytes) { if (!LimeWireUtils.isBetaRelease()) return; totalDropped++; ttls.add(ttl); bytesRouted.add(bytes); } public synchronized void dropTTL0() { // not checking isBeta since these fields are already allocated totalDropped++; droppedTTL0++; } } private static class DuplicateQueryCounter implements Inspectable { private final Buffer<Long> duplicateTimes = new Buffer<Long>(100); private final Buffer<Byte> duplicateHops = new Buffer<Byte>(100); private final Buffer<Byte> duplicateTTLs = new Buffer<Byte>(100); private long totalDropped; private final Buffer<GUID> lastNGUIDs = new Buffer<GUID>(100); private final Map<GUID, Integer> counts = new HashMap<GUID,Integer>(); private int highestEver; public synchronized void duplicateQuery(QueryRequest r) { totalDropped++; if (!LimeWireUtils.isBetaRelease()) return; duplicateTimes.add(System.currentTimeMillis()); duplicateHops.add(r.getHops()); duplicateTTLs.add(r.getTTL()); GUID g = new GUID(r.getGUID()); Integer num = counts.get(g); if (num != null) { num++; highestEver = Math.max(highestEver, num); counts.put(g,num); return; } num = 1; counts.put(g, num); GUID ejected = lastNGUIDs.add(g); if (ejected != null) counts.remove(ejected); } public synchronized Object inspect() { Map<String,Object> ret = new HashMap<String,Object>(); ret.put("total",totalDropped); ret.put("highest",highestEver); byte [] ttlsByte = new byte[duplicateTTLs.getSize()]; for (int i = 0; i < duplicateTTLs.getSize(); i++) ttlsByte[i] = duplicateTTLs.get(i); byte [] hopsByte = new byte[duplicateHops.getSize()]; for (int i = 0; i < duplicateHops.getSize(); i++) hopsByte[i] = duplicateHops.get(i); byte [] timesByte = new byte[duplicateTimes.getSize() * 8]; for (int i = 0; i < duplicateTimes.getSize(); i++) ByteOrder.long2beb(duplicateTimes.get(i), timesByte, i * 8); ret.put("ttls",ttlsByte); ret.put("hops",hopsByte); ret.put("times",timesByte); ret.put("hist", new HashMap<GUID,Integer>(counts)); return ret; } } private class ConnectionListener implements ConnectionLifecycleListener { public void handleConnectionLifecycleEvent(ConnectionLifecycleEvent evt) { if (evt.isConnectionClosedEvent()) { removeConnection(evt.getConnection()); } } } }

The table below shows all metrics for MessageRouterImpl.java.

MetricValueDescription
BLOCKS348.00Number of blocks
BLOCK_COMMENT136.00Number of block comment lines
COMMENTS991.00Comment lines
COMMENT_DENSITY 0.60Comment density
COMPARISONS268.00Number of comparison operators
CYCLOMATIC438.00Cyclomatic complexity
DECL_COMMENTS156.00Comments in declarations
DOC_COMMENT533.00Number of javadoc comment lines
ELOC1645.00Effective lines of code
EXEC_COMMENTS161.00Comments in executable code
EXITS399.00Procedure exits
FUNCTIONS167.00Number of function declarations
HALSTEAD_DIFFICULTY112.14Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY601.00Interface complexity
JAVA0001 0.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 0.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 0.00JAVA0007 Should not declare public field
JAVA0008 2.00JAVA0008 Empty catch block
JAVA0009 0.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 0.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA0020 8.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 2.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 0.00JAVA0029 Private method not used
JAVA0030 1.00JAVA0030 Private field not used
JAVA0031 0.00JAVA0031 Case statement not properly closed
JAVA0032 0.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA003491.00JAVA0034 Missing braces in if statement
JAVA0035 6.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 0.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 1.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA0049 0.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 0.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 0.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 1.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 0.00JAVA0067 Array descriptor on identifier name
JAVA0068 1.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 0.00JAVA0075 Method parameter hides field
JAVA007630.00JAVA0076 Use of magic number
JAVA0077 0.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 0.00JAVA0081 Boolean literal in comparison
JAVA0082 0.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 0.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 2.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field
JAVA0095 0.00JAVA0095 Uninitialized private field
JAVA0096 0.00JAVA0096 Field in nested class hides outer field
JAVA0098 0.00JAVA0098 Minimize use of implicit field initializers
JAVA0100 1.00JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0101 0.00JAVA0101 Unnecessary modifier for field in interface
JAVA0102 0.00JAVA0102 Last statement in finalize() not super.finalize()
JAVA0103 0.00JAVA0103 Explicit call to finalize()
JAVA0104 0.00JAVA0104 finalize() only calls super.finalize()
JAVA0105 0.00JAVA0105 Duplicate import declaration
JAVA0106 0.00JAVA0106 Unnecessary import from current package
JAVA0108128.00JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0109 4.00JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA011011.00JAVA0110 Incorrect javadoc: no @return tag
JAVA0111 1.00JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0112 0.00JAVA0112 Incorrect javadoc: no exception 'exception' in throws
JAVA0113 1.00JAVA0113 Incorrect javadoc: no @author tag
JAVA0114 1.00JAVA0114 Incorrect javadoc: no @version tag
JAVA0115 0.00JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0116 2.00JAVA0116 Missing javadoc: field 'field'
JAVA011710.00JAVA0117 Missing javadoc: method 'method'
JAVA0118 5.00JAVA0118 Missing javadoc: type 'type'
JAVA0119 0.00JAVA0119 Control variable changed within body of for loop
JAVA0123 0.00JAVA0123 Use all three components of for loop
JAVA0125 0.00JAVA0125 Continue statement with label
JAVA0126 2.00JAVA0126 Method declares unchecked exception in throws
JAVA0128 4.00JAVA0128 Public constructor in non-public class
JAVA0130 4.00JAVA0130 Non-static method does not use instance fields
JAVA0131 0.00JAVA0131 Compatible method does not override base
JAVA0132 0.00JAVA0132 Method overload with compatible signature
JAVA0133 0.00JAVA0133 Non-synchronized method overrides synchronized method
JAVA0135 0.00JAVA0135 Only one of Object.equals and Object.hashCode defined: missing 'method'
JAVA0136 1.00JAVA0136 N methods defined in class (maximum: M)
JAVA0137 2.00JAVA0137 Non-abstract class missing constructor
JAVA0138 2.00JAVA0138 N parameters defined for method (maximum: M)
JAVA0139 0.00JAVA0139 Definition of main other than public static void main(java.lang.String[])
JAVA0141 0.00JAVA0141 Unnecessary modifier for method in interface
JAVA0143 6.00JAVA0143 Synchronized method
JAVA014418.00JAVA0144 Line exceeds maximum M characters
JAVA0145833.00JAVA0145 Tab character used in source file
JAVA0150 0.00JAVA0150 java.lang.Error (or subclass) thrown
JAVA0153 0.00JAVA0153 Inefficient conversion of integer to string
JAVA0159 0.00JAVA0159 Inefficient conversion of string to integer
JAVA0160 0.00JAVA0160 Method does not throw specified exception
JAVA0161 0.00JAVA0161 Conditional wait() not in loop
JAVA0163 2.00JAVA0163 Empty statement
JAVA0165 0.00JAVA0165 Conflicting return statement in finally block
JAVA0166 1.00JAVA0166 Generic exception caught
JAVA0167 0.00JAVA0167 ThreadDeath not rethrown
JAVA0169 0.00JAVA0169 Unnecessary catch block: exception 'exception'
JAVA0170 1.00JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0171 0.00JAVA0171 Unused local variable
JAVA0173 4.00JAVA0173 Unused method parameter
JAVA0174 1.00JAVA0174 Assigned local variable never used
JAVA0175 0.00JAVA0175 Successive assignment to variable
JAVA0176 0.00JAVA0176 Local variable name does not have required form
JAVA0177 6.00JAVA0177 Variable declaration missing initializer
JAVA0179 0.00JAVA0179 Local variable hides visible field
JAVA0233 0.00JAVA0233 Definition of serialVersionUID other than 'private static final long serialVersionUID'
JAVA0234 0.00JAVA0234 Class is Serializable but does not define serialVersionUID
JAVA0235 0.00JAVA0235 Class defines serialVersionUID but does not implement Serializable
JAVA0236 0.00JAVA0236 Attempt to clone an object which does not implement Cloneable
JAVA0237 0.00JAVA0237 Class implements Cloneable but does not have public clone method
JAVA0238 0.00JAVA0238 Clone method does not call super.clone()
JAVA0239 0.00JAVA0239 Class declares 'readObject' or 'writeObject' but does not implement Serializable
JAVA0240 0.00JAVA0240 Serializable class which declares readObject or writeObject but not both
JAVA0241 0.00JAVA0241 'readObject' or 'writeObject' should be declared private in Serializable class
JAVA0242 0.00JAVA0242 Transient field in non-Serializable class
JAVA0243 0.00JAVA0243 'readResolve' or 'writeReplace' should be declared private or protected
JAVA0244 0.00JAVA0244 Field or method name in subclass differs only by case from inherited field or method
JAVA0245 0.00JAVA0245 JUnit TestCase with non-trivial constructor
JAVA0246 0.00JAVA0246 JUnit assertXXX statement missing message parameter
JAVA0247 0.00JAVA0247 JUnit 'setUp()' and 'tearDown()' should call super method
JAVA0248 0.00JAVA0248 JUnit method 'setUp' or 'tearDown' with incorrect signature
JAVA0249 0.00JAVA0249 JUnit TestCase 'suite()' should be declared static
JAVA0250 0.00JAVA0250 JUnit TestCase declares testXXX method with incorrect signature
JAVA0251 0.00JAVA0251 Use '%n' for line breaks in printf/format for platform independence
JAVA0252 0.00JAVA0252 'enum' is a Java 1.5 reserved word
JAVA0253 0.00JAVA0253 Not all enum constants consumed in switch statement
JAVA0254 0.00JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0255 0.00JAVA0255 Result of method invocation not used
JAVA0256 2.00JAVA0256 Assignment of external collection/array to field
JAVA0257 0.00JAVA0257 Use of 'Constant Interface' anti-pattern
JAVA0258 0.00JAVA0258 Implement Iterable for foreach compatibility
JAVA0259 0.00JAVA0259 Return of collection/array field
JAVA0260 0.00JAVA0260 Use 'enum' instead of Enumerated Type pattern
JAVA0261 0.00JAVA0261 Use specialized Enum collection types
JAVA0262 0.00JAVA0262 Use of char in integer context
JAVA0263 0.00JAVA0263 Long literal ends with 'l' instead of 'L'
JAVA0264 6.00JAVA0264 Integer math in long context - check for overflow
JAVA0265 0.00JAVA0265 Use of Throwable.printStackTrace()
JAVA0266 0.00JAVA0266 Use of System.out
JAVA0267 0.00JAVA0267 Use of System.err
JAVA0269 0.00JAVA0269 Contents of StringBuffer never used
JAVA0270 0.00JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0271 0.00JAVA0271 Minimize use of on-demand (.*) static imports
JAVA0272 0.00JAVA0272 Thread.run() called
JAVA0273 0.00JAVA0273 Non-final derivative of Thread calls start() in constructor
JAVA0274 0.00JAVA0274 Serializable class has a synchronized readObject()
JAVA0275 0.00JAVA0275 Serializable class has a synchronized writeObject() and no other synchronized methods
JAVA0276 0.00JAVA0276 Unnecessary use of String constructor
JAVA0277 0.00JAVA0277 Iterator.next() implementation does not throw NoSuchElementException
JAVA0278 0.00JAVA0278 Unnecessary use of Boolean constructor
JAVA0279 0.00JAVA0279 Serialization method readObject or readObjectNoData calls an overridable method
JAVA0280 0.00JAVA0280 IllegalMonitorStateException caught
JAVA0281 0.00JAVA0281 Iterator.next() not called in loop
JAVA0282 0.00JAVA0282 Call to Iterator.next() in loop which does not test Iterator.hasNext()
JAVA0283 0.00JAVA0283 Control variable not updated in loop body
JAVA0284 0.00JAVA0284 Explicit garbage collection
JAVA0285 0.00JAVA0285 Dereference of potentially null variable
JAVA0286 0.00JAVA0286 Dereference of null variable
JAVA0287 0.00JAVA0287 Unnecessary null check
JAVA0288 0.00JAVA0288 Inconsistent null check
LINES3424.00Number of lines in the source file
LINE_COMMENT322.00Number of line comments
LOC1994.00Lines of code
LOGICAL_LINES983.00Number of statements
LOOPS22.00Number of loops
NEST_DEPTH 5.00Maximum nesting depth
OPERANDS4973.00Number of operands
OPERATORS9194.00Number of operators
PARAMS340.00Number of formal parameter declarations
PROGRAM_LENGTH14167.00Halstead program length
PROGRAM_VOCAB1599.00Halstead program vocabulary
PROGRAM_VOLUME 0.00Halstead program volume
RETURNS261.00Number of return points from functions
SIZE140180.00Size of the file in bytes
UNIQUE_OPERANDS1530.00Number of unique operands
UNIQUE_OPERATORS69.00Number of unique operators
WHITESPACE439.00Number of whitespace lines