package freenet.node; import java.io.Serializable; import freenet.client.async.ClientContext; import freenet.client.async.ClientRequestScheduler; import freenet.client.async.ClientRequestSchedulerGroup; import freenet.client.async.ClientRequester; import freenet.support.LogThresholdCallback; import freenet.support.Logger; import freenet.support.RandomGrabArray; import freenet.support.RandomGrabArrayItem; import freenet.support.Logger.LogLevel; /** * A low-level request which can be sent immediately. These are registered * on the ClientRequestScheduler. * LOCKING: Because some subclasses may do wierd things like locking on an external object * (see e.g. SplitFileFetcherSubSegment), if we do take the lock we need to do it last i.e. * not call any subclass methods inside it. * * WARNING: Changing non-transient members on classes that are Serializable can result in * restarting downloads or losing uploads. Not all subclasses of this class are actually persisted. */ public abstract class SendableRequest implements RandomGrabArrayItem, Serializable { private static final long serialVersionUID = 1L; /** Since we put these into Set's etc, hashCode must be persistent. * Guaranteed not to be 0 unless this is a persistent object that is deactivated. */ private final int hashCode; protected final boolean realTimeFlag; private static volatile boolean logMINOR; static { Logger.registerLogThresholdCallback(new LogThresholdCallback(){ @Override public void shouldUpdate(){ logMINOR = Logger.shouldLog(LogLevel.MINOR, this); } }); } SendableRequest(boolean persistent, boolean realTimeFlag) { this.persistent = persistent; this.realTimeFlag = realTimeFlag; int oid = super.hashCode(); if(oid == 0) oid = 1; this.hashCode = oid; } @Override public final int hashCode() { return hashCode; } protected transient RandomGrabArray parentGrabArray; /** Member because must be accessible when only marginally activated */ protected final boolean persistent; /** Get the priority class of the request. */ public abstract short getPriorityClass(); /** Choose a key to fetch. Must not modify any persisted structures, but will update cooldowns * etc to avoid it being chosen again soon. There is a separate callback for when a fetch * fails, succeeds, etc. Hence it is safe to call these outside of the PersistentJobRunner. * @return An object identifying a specific key. null indicates no keys available. */ public abstract SendableRequestItem chooseKey(KeysFetchingLocally keys, ClientContext context); /** All key identifiers. Including those not currently eligible to be sent because * they are on a cooldown queue, requests for them are in progress, etc. */ public abstract long countAllKeys(ClientContext context); /** All key identifiers currently eligible to be sent. Does not include those * currently running, on the cooldown queue etc. */ public abstract long countSendableKeys(ClientContext context); /** * Get or create a SendableRequestSender for this object. This is a non-persistent * object used to send the requests. @see SendableGet.getSender(). * @param context A client context may also be necessary. * @return */ public abstract SendableRequestSender getSender(ClientContext context); /** If true, the request has been cancelled, or has completed, either way it need not * be registered any more. isEmpty() on the other hand means there are no queued blocks. */ public abstract boolean isCancelled(); /** Get client context object. This isn't called as frequently as you might expect * - once on registration, and then when there is an error. So it doesn't need to be * stored on the request itself, hence we pass in a container. */ public abstract RequestClient getClient(); /** Is this request persistent? MUST NOT CHANGE. */ public final boolean persistent() { return persistent; } /** Get the ClientRequest. This DOES need to be cached on the request itself. */ public abstract ClientRequester getClientRequest(); @Override public synchronized RandomGrabArray getParentGrabArray() { return parentGrabArray; } /** Grab it to avoid race condition when unregistering twice in parallel. */ private synchronized RandomGrabArray grabParentGrabArray() { RandomGrabArray ret = parentGrabArray; parentGrabArray = null; return ret; } @Override public boolean knowsParentGrabArray() { return true; } @Override public synchronized void setParentGrabArray(RandomGrabArray parent) { parentGrabArray = parent; } /** Unregister the request. * @param context * @param oldPrio If we are changing priorities it can matter what the old priority is. * However the parent method, SendableRequest, ignores this. In any case, * (short)-1 means not specified (look it up). */ public void unregister(ClientContext context, short oldPrio) { RandomGrabArray arr = grabParentGrabArray(); if(arr != null) { synchronized(getScheduler(context)) { arr.remove(this, context); } } else { // Should this be a higher priority? if(logMINOR) Logger.minor(this, "Cannot unregister "+this+" : not registered", new Exception("debug")); } } public abstract ClientRequestScheduler getScheduler(ClientContext context); /** Is this an SSK? For purposes of determining which scheduler to use. */ public abstract boolean isSSK(); /** Is this an insert? For purposes of determining which scheduler to use. */ public abstract boolean isInsert(); /** Requeue after an internal error */ public abstract void internalError(Throwable t, RequestScheduler sched, ClientContext context, boolean persistent); public boolean realTimeFlag() { return realTimeFlag; } protected final String objectToString() { return super.toString(); } @Override public boolean reduceWakeupTime(long wakeupTime, ClientContext context) { RandomGrabArray parent = getParentGrabArray(); if(parent == null) return false; return parent.reduceWakeupTime(wakeupTime, context); } @Override public void clearWakeupTime(ClientContext context) { RandomGrabArray parent = getParentGrabArray(); if(parent == null) return; parent.clearWakeupTime(context); } public ClientRequestSchedulerGroup getSchedulerGroup() { return getClientRequest().getSchedulerGroup(); } }