/* 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.
}
}