package freenet.node.updater;
import java.io.File;
import java.io.IOException;
import freenet.client.FetchContext;
import freenet.client.FetchException;
import freenet.client.FetchResult;
import freenet.client.async.BinaryBlobWriter;
import freenet.client.async.ClientContext;
import freenet.client.async.ClientGetCallback;
import freenet.client.async.ClientGetter;
import freenet.keys.FreenetURI;
import freenet.node.NodeClientCore;
import freenet.node.RequestClient;
import freenet.node.RequestStarter;
import freenet.support.Logger;
import freenet.support.io.FileBucket;
import freenet.support.io.FileUtil;
/** Fetches the old freenet-ext.jar and freenet-stable-latest.jar. In other
* words it fetches the transitional versions.
* @author toad
*/
class LegacyJarFetcher implements ClientGetCallback {
final FreenetURI uri;
final File tempFile;
final File saveTo;
final FileBucket blobBucket;
final FetchContext ctx;
final ClientGetter cg;
final ClientContext context;
private boolean fetched;
private boolean failed;
final LegacyFetchCallback cb;
interface LegacyFetchCallback {
public void onSuccess(LegacyJarFetcher fetcher);
public void onFailure(FetchException e, LegacyJarFetcher fetcher);
}
// Single client for both fetches.
static final RequestClient client = new RequestClient() {
@Override
public boolean persistent() {
return false;
}
@Override
public boolean realTimeFlag() {
return false;
}
};
public LegacyJarFetcher(FreenetURI uri, File saveTo, NodeClientCore core, LegacyFetchCallback cb) {
this.uri = uri;
this.saveTo = saveTo;
this.context = core.clientContext;
this.cb = cb;
ctx = core.makeClient((short) 1, true, false).getFetchContext();
ctx.allowSplitfiles = true;
ctx.dontEnterImplicitArchives = false;
ctx.maxNonSplitfileRetries = -1;
ctx.maxSplitfileBlockRetries = -1;
blobBucket = new FileBucket(saveTo, false, false, false, false);
if(blobBucket.size() > 0) {
fetched = true;
cg = null;
tempFile = null;
} else {
// Write to temp file then rename.
// We do not want to rename unless we are sure we've finished the fetch.
File tmp;
try {
tmp = File.createTempFile(saveTo.getName(), NodeUpdateManager.TEMP_BLOB_SUFFIX, saveTo.getParentFile());
tmp.deleteOnExit(); // To be used sparingly, as it leaks, but safe enough here as it should only happen twice during a normal run.
} catch (IOException e) {
Logger.error(this, "Cannot create temp file so cannot fetch legacy jar "+uri+" : UOM from old versions will not work!");
cg = null;
fetched = false;
tempFile = null;
return;
}
tempFile = tmp;
cg = new ClientGetter(this,
uri, ctx, RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS,
null, new BinaryBlobWriter(new FileBucket(tempFile, false, false, false, false)));
fetched = false;
}
}
public void start() {
boolean f;
synchronized(this) {
f = fetched;
}
if(f)
cb.onSuccess(this);
else {
try {
cg.start(context);
} catch (FetchException e) {
synchronized(this) {
failed = true;
}
cb.onFailure(e, this);
}
}
}
public void stop() {
synchronized(this) {
if(fetched) return;
}
cg.cancel(context);
}
public long getBlobSize() {
if(failed || !fetched) {
Logger.error(this, "Asking for blob size but failed="+failed+" fetched="+fetched);
return -1;
}
return blobBucket.size();
}
public File getBlobFile() {
if(failed || !fetched) {
Logger.error(this, "Asking for blob but failed="+failed+" fetched="+fetched);
return null;
}
return saveTo;
}
/** Have we fetched the key?
* @return True only if we have the blob. */
public synchronized boolean fetched() {
return fetched;
}
public synchronized boolean failed() {
return failed;
}
@Override
public void onSuccess(FetchResult result, ClientGetter state) {
result.asBucket().free();
if(!FileUtil.renameTo(tempFile, saveTo)) {
Logger.error(this, "Fetched file but unable to rename temp file "+tempFile+" to "+saveTo+" : UOM FROM OLD NODES WILL NOT WORK!");
} else {
synchronized(this) {
fetched = true;
}
cb.onSuccess(this);
}
}
@Override
public void onFailure(FetchException e, ClientGetter state) {
synchronized(this) {
failed = true;
}
tempFile.delete();
cb.onFailure(e, this);
}
@Override
public void onResume(ClientContext context) {
// Do nothing. Not persistent.
}
@Override
public RequestClient getRequestClient() {
return client;
}
}