package com.limegroup.gnutella.messagehandlers; import java.net.InetSocketAddress; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.collection.Periodic; import org.limewire.core.settings.FilterSettings; import org.limewire.core.settings.MessageSettings; import org.limewire.io.NetworkInstanceUtils; import org.limewire.security.SecureMessageVerifier; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; import com.limegroup.gnutella.MessageRouter; import com.limegroup.gnutella.NetworkManager; import com.limegroup.gnutella.ReplyHandler; import com.limegroup.gnutella.UDPReplyHandlerFactory; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.vendor.InspectionRequest; import com.limegroup.gnutella.messages.vendor.InspectionResponse; import com.limegroup.gnutella.messages.vendor.InspectionResponseFactory; import com.limegroup.gnutella.simpp.SimppManager; /** * Handles an incoming InspectionRequest, sending a response * if not empty and forwarding the request to leaves if it has a * return address. */ public class InspectionRequestHandler extends RestrictedResponder { private static final Log LOG = LogFactory.getLog(InspectionRequestHandler.class); private final Provider<MessageRouter> router; private final InspectionResponseFactory factory; private final Periodic sender; /** * List of responses to be sent. * LOCKING: this */ private final List<InspectionResponse> queue = new LinkedList<InspectionResponse>(); /** * Where to send the enqueued replies. * (no need to support multiple destinations at present) */ private ReplyHandler currentHandler; /** * The current interval at which to send encoded responses. */ private int currentInterval; @Inject public InspectionRequestHandler(Provider<MessageRouter> router, NetworkManager networkManager, SimppManager simppManager, UDPReplyHandlerFactory udpReplyHandlerFactory, InspectionResponseFactory factory, @Named("inspection") SecureMessageVerifier inspectionVerifier, @Named("messageExecutor") ExecutorService dispatch, @Named("backgroundExecutor") ScheduledExecutorService background, NetworkInstanceUtils networkInstanceUtils) { super(FilterSettings.INSPECTOR_IP_ADDRESSES, inspectionVerifier, MessageSettings.INSPECTION_VERSION, networkManager, simppManager, udpReplyHandlerFactory, dispatch, networkInstanceUtils); this.router = router; this.factory = factory; sender = new Periodic(new Runnable() { public void run() { send(); } }, background); } @Override protected void processAllowedMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) { assert msg instanceof InspectionRequest; InspectionRequest ir = (InspectionRequest)msg; if (LOG.isDebugEnabled()) LOG.debug("processing allowed message" + msg); // send first response back right away InspectionResponse []r = factory.createResponses(ir); if (r.length > 0 && r[0].shouldBeSent()) { if (LOG.isDebugEnabled()) { LOG.debug("sending response: " + r + " to: " + handler); } handler.reply(r[0]); } router.get().forwardInspectionRequestToLeaves(ir); synchronized(this) { // clear any previously scheduled responses queue.clear(); if (r.length < 2) return; // schedule the rest of the responses if any for (int i = 1; i < r.length; i++) queue.add(r[i]); currentHandler = handler; currentInterval = ir.getSendInterval(); } sender.rescheduleIfSooner(currentInterval); } private void send() { InspectionResponse resp = null; ReplyHandler handler = null; int interval; synchronized(this) { if (!queue.isEmpty()) { resp = queue.remove(0); handler = currentHandler; } interval = currentInterval; } if (resp == null || handler == null) { sender.unschedule(); return; } if (resp.shouldBeSent()) { if (LOG.isDebugEnabled()) { LOG.debug("resending response: " + resp + " to: " + handler); } handler.reply(resp); } sender.rescheduleIfLater(interval); } }