package org.limewire.core.impl.browse; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.limewire.core.api.browse.Browse; import org.limewire.core.api.browse.BrowseListener; import org.limewire.core.api.search.SearchResult; import org.limewire.core.impl.search.QueryReplyListener; import org.limewire.core.impl.search.QueryReplyListenerList; import org.limewire.core.impl.search.RemoteFileDescAdapter; import org.limewire.friend.api.FriendPresence; import org.limewire.io.GUID; import org.limewire.io.IpPort; import org.limewire.util.Objects; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.SearchServices; import com.limegroup.gnutella.messages.QueryReply; class CoreBrowse implements Browse { private final SearchServices searchServices; private final FriendPresence friendPresence; private final QueryReplyListenerList listenerList; private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicBoolean stopped = new AtomicBoolean(false); private volatile byte[] browseGuid; private volatile BrowseResultAdapter listener; @Inject public CoreBrowse(@Assisted FriendPresence friendPresence, SearchServices searchServices, QueryReplyListenerList listenerList) { this.friendPresence = Objects.nonNull(friendPresence, "friendPresence"); this.searchServices = searchServices; this.listenerList = listenerList; } @Override public void start(final BrowseListener browseListener) { if (started.getAndSet(true)) { throw new IllegalStateException("already started!"); } browseGuid = searchServices.newQueryGUID(); listener = new BrowseResultAdapter(browseListener); listenerList.addQueryReplyListener(browseGuid, listener); searchServices.doAsynchronousBrowseHost(friendPresence, new GUID(browseGuid), new ListenerProxy(browseListener)); } @Override public void stop() { // if the listener hadn't already stopped, // this is a 'cancel' which we'll consider a failed browse. if(!stopped.getAndSet(true)) { if(listener != null) { listener.browseListener.browseFinished(false); } } // TODO: This should cancel the browse if it was active. listenerList.removeQueryReplyListener(browseGuid, listener); searchServices.stopQuery(new GUID(browseGuid)); } private class ListenerProxy implements BrowseListener { private final BrowseListener delegate; public ListenerProxy(BrowseListener delegate) { this.delegate = delegate; } @Override public void browseFinished(boolean success) { // only push to delegate if this hasn't already been stoppeed. if(!stopped.getAndSet(true)) { delegate.browseFinished(success); } stop(); } @Override public void handleBrowseResult(SearchResult searchResult) { if(!stopped.get()) { delegate.handleBrowseResult(searchResult); } } } private static class BrowseResultAdapter implements QueryReplyListener { private final BrowseListener browseListener; public BrowseResultAdapter(BrowseListener browseListener) { this.browseListener = browseListener; } @Override public void handleQueryReply(RemoteFileDesc rfd, QueryReply queryReply, Set<? extends IpPort> locs) { browseListener.handleBrowseResult(new RemoteFileDescAdapter(rfd, locs)); } } }