/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.node; import freenet.client.InsertException; import freenet.client.async.ChosenBlock; import freenet.client.async.ClientContext; import freenet.client.async.ClientRequestScheduler; import freenet.client.async.ClientRequestSchedulerGroup; import freenet.client.async.ClientRequester; import freenet.keys.CHKBlock; import freenet.keys.ClientKey; import freenet.keys.KeyBlock; import freenet.keys.SSKBlock; import freenet.support.LogThresholdCallback; import freenet.support.Logger; import freenet.support.Logger.LogLevel; /** * Simple SendableInsert implementation. No feedback, no retries, just insert the * block. Not designed for use by the client layer (and not persistent). Used by the node layer * for the 1 in every 200 successful requests which starts an insert. */ public class SimpleSendableInsert extends SendableInsert { private static final long serialVersionUID = 1L; public final KeyBlock block; public final short prioClass; private boolean finished; public final RequestClient client; public final ClientRequestScheduler scheduler; private static volatile boolean logMINOR; static { Logger.registerLogThresholdCallback(new LogThresholdCallback(){ @Override public void shouldUpdate(){ logMINOR = Logger.shouldLog(LogLevel.MINOR, this); } }); } public SimpleSendableInsert(NodeClientCore core, KeyBlock block, short prioClass) { super(false, false); this.block = block; this.prioClass = prioClass; this.client = core.node.nonPersistentClientBulk; if(block instanceof CHKBlock) scheduler = core.requestStarters.chkPutSchedulerBulk; else if(block instanceof SSKBlock) scheduler = core.requestStarters.sskPutSchedulerBulk; else throw new IllegalArgumentException("Don't know what to do with "+block); if(!scheduler.isInsertScheduler()) throw new IllegalStateException("Scheduler "+scheduler+" is not an insert scheduler!"); } public SimpleSendableInsert(KeyBlock block, short prioClass, RequestClient client, ClientRequestScheduler scheduler) { super(false, false); this.block = block; this.prioClass = prioClass; this.client = client; this.scheduler = scheduler; } @Override public void onSuccess(SendableRequestItem keyNum, ClientKey key, ClientContext context) { // Yay! if(logMINOR) Logger.minor(this, "Finished insert of "+block); } @Override public void onFailure(LowLevelPutException e, SendableRequestItem keyNum, ClientContext context) { if(logMINOR) Logger.minor(this, "Failed insert of "+block+": "+e); } @Override public short getPriorityClass() { return prioClass; } @Override public SendableRequestSender getSender(ClientContext context) { return new SendableRequestSender() { @Override public boolean send(NodeClientCore core, RequestScheduler sched, ClientContext context, ChosenBlock req) { // Ignore keyNum, key, since this is a single block try { if(logMINOR) Logger.minor(this, "Starting request: "+this); // FIXME bulk flag core.realPut(block, req.canWriteClientCache, Node.FORK_ON_CACHEABLE_DEFAULT, Node.PREFER_INSERT_DEFAULT, Node.IGNORE_LOW_BACKOFF_DEFAULT, false); } catch (LowLevelPutException e) { onFailure(e, req.token, context); if(logMINOR) Logger.minor(this, "Request failed: "+this+" for "+e); return true; } finally { finished = true; } if(logMINOR) Logger.minor(this, "Request succeeded: "+this); onSuccess(req.token, null, context); sched.removeRunningInsert(SimpleSendableInsert.this, req.token.getKey()); return true; } @Override public boolean sendIsBlocking() { return true; } }; } @Override public RequestClient getClient() { return client; } @Override public ClientRequester getClientRequest() { return null; } @Override public ClientRequestSchedulerGroup getSchedulerGroup() { return null; } @Override public boolean isCancelled() { return finished; } @Override public boolean isEmpty() { return finished; } public void schedule() { finished = false; // can reschedule scheduler.registerInsert(this, false); } public void cancel(ClientContext context) { synchronized(this) { if(finished) return; finished = true; } super.unregister(context, prioClass); } @Override public synchronized long countAllKeys(ClientContext context) { if(finished) return 0; return 1; } @Override public synchronized long countSendableKeys(ClientContext context) { if(finished) return 0; return 1; } // FIXME share with SingleBlockInserter??? private static class MySendableRequestItem implements SendableRequestItem, SendableRequestItemKey { final SimpleSendableInsert parent; public MySendableRequestItem(SimpleSendableInsert parent) { this.parent = parent; } @Override public void dump() { // Ignore. } @Override public SendableRequestItemKey getKey() { return this; } @Override public boolean equals(Object o) { if(o instanceof MySendableRequestItem) { return ((MySendableRequestItem)o).parent == parent; } else return false; } @Override public int hashCode() { return parent.hashCode(); } } @Override public synchronized SendableRequestItem chooseKey(KeysFetchingLocally keys, ClientContext context) { MySendableRequestItem mine = new MySendableRequestItem(this); if(keys.hasInsert(mine)) return null; if(finished) return null; else return mine; } @Override public synchronized long getWakeupTime(ClientContext context, long now) { if(isEmpty()) return -1; if(scheduler.fetchingKeys().hasInsert(new MySendableRequestItem(this))) return Long.MAX_VALUE; return 0; } @Override public boolean isSSK() { return block instanceof SSKBlock; } @Override public boolean canWriteClientCache() { return false; } @Override public boolean forkOnCacheable() { return Node.FORK_ON_CACHEABLE_DEFAULT; } @Override public void onEncode(SendableRequestItem token, ClientKey key, ClientContext context) { // Ignore. } @Override public boolean localRequestOnly() { return false; } @Override protected void innerOnResume(ClientContext context) throws InsertException { // Do nothing. } }