package com.limegroup.gnutella.dht.db; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.mojito.EntityKey; import org.limewire.mojito.KUID; import org.limewire.mojito.concurrent.DHTFuture; import org.limewire.mojito.concurrent.DHTFutureAdapter; import org.limewire.mojito.db.DHTValueEntity; import org.limewire.mojito.db.DHTValueType; import org.limewire.mojito.result.FindValueResult; import com.limegroup.gnutella.dht.DHTManager; /** * An abstract implementation of DHTFutureAdapter to handle AltLocValues * and PushAltLocValues. * * Iterates over entity keys and retrieves their values from the node and * calls {@link #handleDHTValueEntity(DHTValueEntity)} for them. */ abstract class AbstractResultHandler extends DHTFutureAdapter<FindValueResult> { private static final Log LOG = LogFactory.getLog(AbstractResultHandler.class); /** * Result type to denote different results of {@link AbstractResultHandler#handleDHTValueEntity(DHTValueEntity)}. */ enum Result { /** An alternate location has been found */ FOUND(true), /** An alternate location has not been found and will not be found */ NOT_FOUND(false), /** An alternate location has not yet been found but could still be found */ NOT_YET_FOUND(false) { @Override public boolean isFound() { throw new UnsupportedOperationException("Should not have been called on " + this); } }; private final boolean value; private Result(boolean value) { this.value = value; } public boolean isFound() { return value; } }; protected final DHTManager dhtManager; protected final KUID key; private final SearchListener listener; protected final DHTValueType valueType; AbstractResultHandler(DHTManager dhtManager, KUID key, SearchListener listener, DHTValueType valueType) { this.dhtManager = dhtManager; this.key = key; this.listener = listener; this.valueType = valueType; if (listener == null) { throw new NullPointerException("listener should not be null"); } } @Override public void handleFutureSuccess(FindValueResult result) { if (LOG.isDebugEnabled()) { LOG.debug("result: " + result); } Result outcome = Result.NOT_FOUND; if (result.isSuccess()) { for (DHTValueEntity entity : result.getEntities()) { outcome = updateResult(outcome, handleDHTValueEntity(entity)); } for (EntityKey entityKey : result.getEntityKeys()) { if (!entityKey.getDHTValueType().equals(valueType)) { continue; } DHTFuture<FindValueResult> future = dhtManager.get(entityKey); if(future != null) { try { // TODO make this a non-blocking call FindValueResult resultFromKey = future.get(); if (LOG.isDebugEnabled()) { LOG.debug("result from second lookup: " + resultFromKey); } if (resultFromKey.isSuccess()) { for (DHTValueEntity entity : resultFromKey.getEntities()) { outcome = updateResult(outcome, handleDHTValueEntity(entity)); } } } catch (ExecutionException e) { LOG.error("ExecutionException", e); } catch (InterruptedException e) { LOG.error("InterruptedException", e); } } } } // only notify if we haven't found anything if (outcome == Result.NOT_FOUND) { LOG.debug("Not found"); listener.searchFailed(); } } /** * Updates the result from the old value to the new value if the new value * doesn't already say the value was found. * * FOUND => goes nowhere, the value has been found * NOT_YET_FOUND => can go to FOUND * NOT_FOUND => can go to NOT_YET_FOUND, or FOUND, or stay with FOUND */ private Result updateResult(Result oldValue, Result possibleValue) { if (oldValue == Result.FOUND || possibleValue == Result.FOUND) { return Result.FOUND; } else if (oldValue == Result.NOT_YET_FOUND || possibleValue == Result.NOT_YET_FOUND) { return Result.NOT_YET_FOUND; } else { return possibleValue; } } /** * Handles a DHTValueEntity (turns it into some Gnutella Object) * and returns true on success */ protected abstract Result handleDHTValueEntity(DHTValueEntity entity); @Override public void handleCancellationException(CancellationException e) { LOG.error("CancellationException", e); listener.searchFailed(); } @Override public void handleExecutionException(ExecutionException e) { LOG.error("ExecutionException", e); listener.searchFailed(); } @Override public void handleInterruptedException(InterruptedException e) { LOG.error("InterruptedException", e); listener.searchFailed(); } @Override public int hashCode() { return key.hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (!(o instanceof AbstractResultHandler)) { return false; } AbstractResultHandler other = (AbstractResultHandler)o; return key.equals(other.key) && valueType.equals(other.valueType); } }