/* 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.client.async;
import java.util.HashMap;
import java.util.Map;
import freenet.client.InsertContext;
import freenet.client.InsertException;
import freenet.client.Metadata;
import freenet.keys.BaseClientKey;
import freenet.keys.CHKBlock;
import freenet.keys.FreenetURI;
import freenet.node.RequestClient;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;
import freenet.support.api.Bucket;
public class SimpleHealingQueue extends BaseClientPutter implements HealingQueue, PutCompletionCallback, ClientRequestSchedulerGroup {
final int maxRunning;
int counter;
InsertContext ctx;
final Map<Bucket, SingleBlockInserter> runningInserters;
private static volatile boolean logMINOR;
static {
Logger.registerLogThresholdCallback(new LogThresholdCallback(){
@Override
public void shouldUpdate(){
logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
}
});
}
static final RequestClient REQUEST_CLIENT =
new RequestClient() {
@Override
public boolean persistent() {
return false;
}
@Override
public boolean realTimeFlag() {
return false;
}
};
static final ClientBaseCallback BOGUS_CALLBACK =
new ClientBaseCallback() {
@Override
public void onResume(ClientContext context) {
throw new IllegalStateException(); // Impossible.
}
@Override
public RequestClient getRequestClient() {
return REQUEST_CLIENT;
}
};
public SimpleHealingQueue(InsertContext context, short prio, int maxRunning) {
super(prio, BOGUS_CALLBACK);
this.ctx = context;
this.runningInserters = new HashMap<Bucket, SingleBlockInserter>();
this.maxRunning = maxRunning;
}
public boolean innerQueue(Bucket data, byte[] cryptoKey, byte cryptoAlgorithm, ClientContext context) {
SingleBlockInserter sbi;
int ctr;
synchronized(this) {
ctr = counter++;
if(runningInserters.size() > maxRunning) return false;
try {
sbi = new SingleBlockInserter(this, data, (short)-1,
FreenetURI.EMPTY_CHK_URI, ctx, realTimeFlag, this, false,
CHKBlock.DATA_LENGTH, ctr, false, false, data, context, false, true, 0, cryptoAlgorithm, cryptoKey);
} catch (Throwable e) {
Logger.error(this, "Caught trying to insert healing block: "+e, e);
return false;
}
runningInserters.put(data, sbi);
}
try {
sbi.schedule(context);
if(logMINOR)
Logger.minor(this, "Started healing insert "+ctr+" for "+data);
return true;
} catch (Throwable e) {
Logger.error(this, "Caught trying to insert healing block: "+e, e);
return false;
}
}
@Override
public void queue(Bucket data, byte[] cryptoKey, byte cryptoAlgorithm, ClientContext context) {
if(!innerQueue(data, cryptoKey, cryptoAlgorithm, context))
data.free();
}
@Override
public FreenetURI getURI() {
return FreenetURI.EMPTY_CHK_URI;
}
@Override
public boolean isFinished() {
return false;
}
@Override
protected void innerNotifyClients(ClientContext context) {
// Do nothing
}
@Override
public void onSuccess(ClientPutState state, ClientContext context) {
SingleBlockInserter sbi = (SingleBlockInserter)state;
Bucket data = (Bucket) sbi.getToken();
synchronized(this) {
runningInserters.remove(data);
}
if(logMINOR)
Logger.minor(this, "Successfully inserted healing block: "+sbi.getURINoEncode()+" for "+data+" ("+sbi.token+ ')');
data.free();
}
@Override
public void onFailure(InsertException e, ClientPutState state, ClientContext context) {
SingleBlockInserter sbi = (SingleBlockInserter)state;
Bucket data = (Bucket) sbi.getToken();
synchronized(this) {
runningInserters.remove(data);
}
if(logMINOR)
Logger.minor(this, "Failed to insert healing block: "+sbi.getURINoEncode()+" : "+e+" for "+data+" ("+sbi.token+ ')', e);
data.free();
}
@Override
public void onEncode(BaseClientKey usk, ClientPutState state, ClientContext context) {
// Ignore
}
@Override
public void onTransition(ClientPutState oldState, ClientPutState newState, ClientContext context) {
// Should never happen
Logger.error(this, "impossible: onTransition on SimpleHealingQueue from "+oldState+" to "+newState, new Exception("debug"));
}
@Override
public void onMetadata(Metadata m, ClientPutState state, ClientContext context) {
// Should never happen
Logger.error(this, "Got metadata on SimpleHealingQueue from "+state+": "+m, new Exception("debug"));
}
@Override
public void onBlockSetFinished(ClientPutState state, ClientContext context) {
// Ignore
}
@Override
public void onFetchable(ClientPutState state) {
// Ignore
}
@Override
public void onTransition(ClientGetState oldState, ClientGetState newState, ClientContext context) {
// Ignore
}
@Override
protected void innerToNetwork(ClientContext context) {
// Ignore
}
@Override
public void cancel(ClientContext context) {
super.cancel();
}
@Override
public int getMinSuccessFetchBlocks() {
return 0;
}
@Override
public void onMetadata(Bucket meta, ClientPutState state,
ClientContext context) {
Logger.error(this, "onMetadata() in SimpleHealingQueue - impossible", new Exception("error"));
meta.free();
}
@Override
public void innerOnResume(ClientContext context) {
// Do nothing. Not persisted.
}
@Override
protected ClientBaseCallback getCallback() {
return null;
}
@Override
public ClientRequestSchedulerGroup getSchedulerGroup() {
return this;
}
}