Package freenet.clients.fcp

Source Code of freenet.clients.fcp.ClientRequest

package freenet.clients.fcp;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;

import freenet.client.async.ClientContext;
import freenet.client.async.ClientRequester;
import freenet.client.async.PersistenceDisabledException;
import freenet.client.async.PersistentJob;
import freenet.crypt.ChecksumChecker;
import freenet.keys.FreenetURI;
import freenet.node.PrioRunnable;
import freenet.node.RequestClient;
import freenet.node.RequestStarter;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;
import freenet.support.io.NativeThread;
import freenet.support.io.ResumeFailedException;
import freenet.support.io.StorageFormatException;

/**
* A request process carried out by the node for an FCP client.
* Examples: ClientGet, ClientPut, MultiGet.
*/
public abstract class ClientRequest implements Serializable {

    private static final long serialVersionUID = 1L;
    /** URI to fetch, or target URI to insert to */
  protected FreenetURI uri;
  /** Unique request identifier */
  protected final String identifier;
  /** Verbosity level. Relevant to all ClientRequests, although they interpret it
   * differently. */
  protected final int verbosity;
  /** Original FCPConnectionHandler. Null if persistence != connection */
  protected transient final FCPConnectionHandler origHandler;
  /** Is the request on the global queue? */
    protected final boolean global;
    /** If the request isn't on the global queue, what is the client's name? */
    protected final String clientName;
  /** Client */
  protected transient PersistentRequestClient client;
  /** Priority class */
  protected short priorityClass;
  /** Is the request scheduled as "real-time" (as opposed to bulk)? */
  protected final boolean realTime;
  /** Persistence type */
  protected final Persistence persistence;
  /** Has the request finished? */
  protected boolean finished;
  /** Client token (string to feed back to the client on a Persistent* when he does a
   * ListPersistentRequests). */
  protected String clientToken;
  /** Timestamp : startup time */
  protected final long startupTime;
  /** Timestamp : completion time */
  protected long completionTime;

  /** Timestamp: last activity. */
  protected long lastActivity;

  protected transient RequestClient lowLevelClient;
  private final int hashCode; // for debugging it is good to have a persistent id
 
  @Override
  public int hashCode() {
    return hashCode;
  }

  private static volatile boolean logMINOR;
 
  static {
    Logger.registerLogThresholdCallback(new LogThresholdCallback() {
     
      @Override
      public void shouldUpdate() {
        logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
      }
    });
  }

  public ClientRequest(FreenetURI uri2, String identifier2, int verbosity2, String charset,
      FCPConnectionHandler handler, PersistentRequestClient client, short priorityClass2, Persistence persistenceType2, boolean realTime, String clientToken2, boolean global) {
    int hash = super.hashCode();
    if(hash == 0) hash = 1;
    hashCode = hash;
    this.uri = uri2;
    this.identifier = identifier2;
    if(global) {
      this.verbosity = Integer.MAX_VALUE;
      this.clientName = null;
    } else {
      this.verbosity = verbosity2;
      this.clientName = client.name;
      }
    this.finished = false;
    this.priorityClass = priorityClass2;
    this.persistence = persistenceType2;
    this.clientToken = clientToken2;
    this.global = global;
    if(persistence == Persistence.CONNECTION) {
      this.origHandler = handler;
      lowLevelClient = origHandler.connectionRequestClient(realTime);
      this.client = null;
    } else {
      origHandler = null;
      this.client = client;
      assert client != null;
      assert(client.persistence == persistence);
      lowLevelClient = client.lowLevelClient(realTime);
    }
    assert lowLevelClient != null;
    this.startupTime = System.currentTimeMillis();
    this.realTime = realTime;
  }

  public ClientRequest(FreenetURI uri2, String identifier2, int verbosity2, String charset,
      FCPConnectionHandler handler, short priorityClass2, Persistence persistenceType2, final boolean realTime, String clientToken2, boolean global) {
    int hash = super.hashCode();
    if(hash == 0) hash = 1;
    hashCode = hash;
    this.uri = uri2;
   
    this.identifier = identifier2;
    this.finished = false;
    this.priorityClass = priorityClass2;
    this.persistence = persistenceType2;
    this.clientToken = clientToken2;
    this.global = global;
    if(persistence == Persistence.CONNECTION) {
      this.origHandler = handler;
      client = null;
      lowLevelClient = new RequestClient() {

        @Override
        public boolean persistent() {
          return false;
        }

        @Override
        public boolean realTimeFlag() {
          return realTime;
        }
       
      };
      this.clientName = null;
            this.verbosity = verbosity2;
    } else {
      origHandler = null;
      if(global) {
        client = persistence == Persistence.FOREVER ? handler.server.globalForeverClient : handler.server.globalRebootClient;
              this.verbosity = Integer.MAX_VALUE;
              clientName = null;
      } else {
        client = persistence == Persistence.FOREVER ? handler.getForeverClient() : handler.getRebootClient();
              this.verbosity = verbosity2;
              this.clientName = client.name;
      }
      lowLevelClient = client.lowLevelClient(realTime);
      if(lowLevelClient == null)
        throw new NullPointerException("No lowLevelClient from client: "+client+" global = "+global+" persistence = "+persistence);
    }
    if(lowLevelClient.persistent() != (persistence == Persistence.FOREVER))
      throw new IllegalStateException("Low level client.persistent="+lowLevelClient.persistent()+" but persistence type = "+persistence);
    if(client != null)
      assert(client.persistence == persistence);
    this.startupTime = System.currentTimeMillis();
    this.realTime = realTime;
  }
 
  protected ClientRequest() {
      // For serialization.
      identifier = null;
      verbosity = 0;
      origHandler = null;
      global = false;
      clientName = null;
      realTime = false;
      persistence = null;
      startupTime = 0;
      hashCode = 0;
  }

    /** Lost connection */
  public abstract void onLostConnection(ClientContext context);

  /** Send any pending messages for a persistent request e.g. after reconnecting */
  public abstract void sendPendingMessages(FCPConnectionOutputHandler handler, boolean includePersistentRequest, boolean includeData, boolean onlyData);

  // Persistence

  public enum Persistence {
        /** Default: persists until connection loss. */
      CONNECTION,
      /** Reports to client by name; persists over connection loss.
       * Not saved to disk, so dies on reboot. */
      REBOOT,
        /** Same as reboot but saved to disk, persists forever. */
      FOREVER;

        public static Persistence parseOrThrow(String persistenceString, String identifier, boolean global) throws MessageInvalidException {
            try {
                if(persistenceString == null) return Persistence.CONNECTION;
                else return Persistence.valueOf(persistenceString.toUpperCase());
            } catch (IllegalArgumentException e) {
                throw new MessageInvalidException(ProtocolErrorMessage.ERROR_PARSING_NUMBER, "Error parsing Persistence field: "+persistenceString, identifier, global);
            }
        }

        @Deprecated // Only for migration
        public static Persistence getByCode(short persistenceType) {
            if(persistenceType < 0 || persistenceType > values().length) throw new IllegalArgumentException();
            return values()[persistenceType];
        }
  }

  abstract void register(boolean noTags) throws IdentifierCollisionException;

  public void cancel(ClientContext context) {
    ClientRequester cr = getClientRequest();
    // It might have been finished on startup.
    if(logMINOR) Logger.minor(this, "Cancelling "+cr+" for "+this+" persistence = "+persistence);
    if(cr != null) cr.cancel(context);
    freeData();
  }

  public boolean isPersistentForever() {
    return persistence == Persistence.FOREVER;
  }

  /** Is the request persistent? False = we can drop the request if we lose the connection */
  public boolean isPersistent() {
    return persistence != Persistence.CONNECTION;
  }

  public boolean hasFinished() {
    return finished;
  }

  /** Get identifier string for request */
  public String getIdentifier() {
    return identifier;
  }

  protected abstract ClientRequester getClientRequest();

  /** Completed request dropped off the end without being acknowledged */
  public void dropped(ClientContext context) {
    cancel(context);
    freeData();
  }

  /** Return the priority class */
  public short getPriority(){
    return priorityClass;
  }

  /** Free cached data bucket(s) */
  protected abstract void freeData();

  /** Request completed. But we may have to stick around until we are acked. */
  protected void finish() {
    if(persistence == Persistence.CONNECTION)
      origHandler.finishedClientRequest(this);
    else
      client.finishedClientRequest(this);
  }

  public abstract double getSuccessFraction();

  public abstract double getTotalBlocks();
  public abstract double getMinBlocks();
  public abstract double getFetchedBlocks();
  public abstract double getFailedBlocks();
  public abstract double getFatalyFailedBlocks();

  public abstract String getFailureReason(boolean longDescription);

  /**
   * Has the total number of blocks to insert been determined yet?
   */
  public abstract boolean isTotalFinalized();

  /** Start the request, if it has not already been started. */
  public abstract void start(ClientContext context);

  protected boolean started;

  public boolean isStarted() {
    return started;
  }

  public abstract boolean hasSucceeded();

  /**
   * Returns the time of the request’s last activity, or {@code 0} if there is
   * no known last activity.
   *
   * @return The time of the request’s last activity, or {@code 0}
   */
  public long getLastActivity() {
    return lastActivity;
  }

  public abstract boolean canRestart();

  public abstract boolean restart(ClientContext context, boolean disableFilterData) throws PersistenceDisabledException;

  protected abstract FCPMessage persistentTagMessage();

  /**
   * Called after a ModifyPersistentRequest.
   * Sends a PersistentRequestModified message to clients if any value changed.
   */
  public void modifyRequest(String newClientToken, short newPriorityClass, FCPServer server) {

    boolean clientTokenChanged = false;
    boolean priorityClassChanged = false;

    if(newClientToken != null) {
      if( clientToken != null ) {
        if( !newClientToken.equals(clientToken) ) {
          this.clientToken = newClientToken; // token changed
          clientTokenChanged = true;
        }
      } else {
        this.clientToken = newClientToken; // first time the token is set
        clientTokenChanged = true;
      }
    }
   
    if(newPriorityClass >= 0 && newPriorityClass != priorityClass) {
      this.priorityClass = newPriorityClass;
      ClientRequester r = getClientRequest();
      r.setPriorityClass(priorityClass, server.core.clientContext);
      priorityClassChanged = true;
      if(client != null) {
        RequestStatusCache cache = client.getRequestStatusCache();
        if(cache != null) {
          cache.setPriority(identifier, newPriorityClass);
        }
      }
    }

    if(! ( clientTokenChanged || priorityClassChanged ) ) {
      return; // quick return, nothing was changed
    }
   
    server.core.clientContext.jobRunner.setCheckpointASAP();
   
    // this could become too complex with more parameters, but for now its ok
    final PersistentRequestModifiedMessage modifiedMsg;
    if( clientTokenChanged && priorityClassChanged ) {
      modifiedMsg = new PersistentRequestModifiedMessage(identifier, global, priorityClass, clientToken);
    } else if( priorityClassChanged ) {
      modifiedMsg = new PersistentRequestModifiedMessage(identifier, global, priorityClass);
    } else if( clientTokenChanged ) {
      modifiedMsg = new PersistentRequestModifiedMessage(identifier, global, clientToken);
    } else {
      return; // paranoia, we should not be here if nothing was changed!
    }
    client.queueClientRequestMessage(modifiedMsg, 0);
  }

  public void restartAsync(final FCPServer server, final boolean disableFilterData) throws PersistenceDisabledException {
    synchronized(this) {
      this.started = false;
    }
    if(client != null) {
      RequestStatusCache cache = client.getRequestStatusCache();
      if(cache != null) {
        cache.updateStarted(identifier, false);
      }
    }
    if(persistence == Persistence.FOREVER) {
    server.core.clientContext.jobRunner.queue(new PersistentJob() {

      @Override
      public boolean run(ClientContext context) {
          try {
              restart(context, disableFilterData);
          } catch (PersistenceDisabledException e) {
              // Impossible
          }
        return true;
      }
     
    }, NativeThread.HIGH_PRIORITY);
    } else {
      server.core.getExecutor().execute(new PrioRunnable() {

        @Override
        public int getPriority() {
          return NativeThread.NORM_PRIORITY;
        }

        @Override
        public void run() {
            try {
                        restart(server.core.clientContext, disableFilterData);
                    } catch (PersistenceDisabledException e) {
                        // Impossible
                    }
        }
       
      }, "Restart request");
    }
  }

  /**
   * Called after a RemovePersistentRequest. Send a PersistentRequestRemoved to the clients.
   * If the request is in the database, delete it.
   */
  public void requestWasRemoved(ClientContext context) {
    if(persistence != Persistence.FOREVER) return;
  }

  protected boolean isGlobalQueue() {
    if(client == null) return false;
    return client.isGlobalQueue;
  }

  public PersistentRequestClient getClient(){
    return client;
  }

  abstract RequestStatus getStatus();
 
  private static final long CLIENT_DETAIL_MAGIC = 0xebf0b4f4fa9f6721L;
  private static final int CLIENT_DETAIL_VERSION = 1;

    public void getClientDetail(DataOutputStream dos, ChecksumChecker checker) throws IOException {
        if(persistence != Persistence.FOREVER) return;
        dos.writeLong(CLIENT_DETAIL_MAGIC);
        dos.writeInt(CLIENT_DETAIL_VERSION);
        // Identify the request first.
        RequestIdentifier req = getRequestIdentifier();
        req.writeTo(dos);
        // Basic details needed for scheduling, reporting and completion.
        dos.writeBoolean(realTime);
        dos.writeInt(verbosity);
        dos.writeLong(startupTime);
        // persistence is assumed to be PERSIST_FOREVER.
        // uri will be handled by subclasses.
        // This can change.
        dos.writeShort(priorityClass);
        // This can change and is variable size.
        if(clientToken == null)
            dos.writeBoolean(false);
        else {
            dos.writeBoolean(true);
            dos.writeUTF(clientToken);
        }
        // Stuff that changes on completion
        dos.writeBoolean(finished);
    }
   
    protected ClientRequest(DataInputStream dis, RequestIdentifier reqID,
            ClientContext context) throws IOException, StorageFormatException {
        long magic = dis.readLong();
        if(magic != CLIENT_DETAIL_MAGIC)
            throw new StorageFormatException("Bad magic");
        int version = dis.readInt();
        if(version != CLIENT_DETAIL_VERSION)
            throw new StorageFormatException("Bad version");
        RequestIdentifier copyReq = new RequestIdentifier(dis);
        if(!copyReq.equals(reqID))
            throw new StorageFormatException("Request identifier has changed");
        realTime = dis.readBoolean();
        verbosity = dis.readInt();
        startupTime = dis.readLong();
        priorityClass = dis.readShort();
        if(priorityClass < RequestStarter.MAXIMUM_PRIORITY_CLASS ||
                priorityClass > RequestStarter.PAUSED_PRIORITY_CLASS)
            throw new StorageFormatException("Bogus priority");
        if(dis.readBoolean())
            clientToken = dis.readUTF();
        else
            clientToken = null;
        finished = dis.readBoolean();
        persistence = Persistence.FOREVER;
        origHandler = null;
        identifier = reqID.identifier;
        global = reqID.globalQueue;
        clientName = reqID.clientName;
        hashCode = super.hashCode();
        // We can't wait until onResume() to get the client, because it may be used in the
        // constructors.
        this.client = context.persistentRoot.makeClient(global, clientName);
        this.lowLevelClient = client.lowLevelClient(realTime);
    }

    /** Called just after serializing in the request. Called by the ClientRequester, i.e. the tree
     * starts there, and we MUST NOT call back to it or we get an infinite recursion. The main
     * purpose of this method is to give us an opportunity to connect to the various (transient)
     * system utilities we get from ClientContext, e.g. bucket factories, the FCP persistent root
     * etc. The base class implementation in ClientRequest will register the request with an
     * PersistentRequestClient via the new PersistentRequestRoot.
     * @param context Contains all the important system utilities.
     * @throws ResumeFailedException
     */
    public final void onResume(ClientContext context) throws ResumeFailedException {
        client = context.persistentRoot.makeClient(global, clientName);
        lowLevelClient = client.lowLevelClient(realTime);
        innerResume(context);
        ClientRequester req = getClientRequest();
        if(req != null) req.onResume(context); // Can legally be null.
        context.persistentRoot.resume(this, global, clientName);
    }
   
    protected abstract void innerResume(ClientContext context) throws ResumeFailedException;

    public RequestClient getRequestClient() {
        return lowLevelClient;
    }

    /** Get the RequestIdentifier. This just includes the queue and the identifier. */
    public RequestIdentifier getRequestIdentifier() {
        if(persistence == Persistence.CONNECTION) throw new IllegalStateException(); // Not associated with any client.
        return new RequestIdentifier(global, clientName, identifier, getType());
    }
   
    abstract RequestIdentifier.RequestType getType();

    public static ClientRequest restartFrom(DataInputStream dis, RequestIdentifier reqID,
            ClientContext context, ChecksumChecker checker) throws StorageFormatException, IOException, ResumeFailedException {
        switch(reqID.type) {
        case GET:
            return ClientGet.restartFrom(dis, reqID, context, checker);
        default:
            return null;
        }
    }

    /** Return true if we resumed the original fetch from stored data (usually a file for a
     * splitfile download), rather than having to restart it (which happens in most other cases
     * when we resume). */
    public abstract boolean fullyResumed();

    /** Called just before the final write when the node is shutting down. Should write any dirty
     * data to disk etc. */
    public void onShutdown(ClientContext context) {
        ClientRequester request = getClientRequest();
        if(request != null)
            request.onShutdown(context);
    }
}
TOP

Related Classes of freenet.clients.fcp.ClientRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.