Class FileUploadRegistry

java.lang.Object
com.iizix.server.upload.FileUploadRegistry

public final class FileUploadRegistry extends Object
The server-global registry of live upload reservations: mints them, enforces the quotas at reservation time, holds the per-reservation locks, arms the start and idle timeouts, records accepted chunks, and cancels.

Quota is checked here and never at write time. A reservation holds its accepted files' declared sizes against the session and server quotas until each file reaches a terminal state; a never-started reservation is reclaimed by the start timeout, an abandoned one by the idle timeout.

Threading: reserve(String, String, List, UploadLimits, IFileUploadListener, boolean) synchronizes on the registry for the quota computation; every per-reservation change runs under that reservation's lock. Timeouts fire on the ITimeouts implementation's thread and take the reservation lock themselves. Listener dispatch is not done here: the FileUploadRegistry.IEvents callback is told, and the application-session service posts to its worker.

Pause: a file is paused by the App (pause(UploadReservation, int, String, boolean)) or by the client's notice, each with its own flag on the record; the state is UploadState.PAUSED while either is set and returns to where it was when both are clear. When no file of a reservation is receiving and at least one is paused, the reservation is paused as a whole: its start and idle timeouts are cancelled and the maximum pause timeout runs; when a file receives again, the idle timeout (or the start timeout, if no chunk has ever arrived) is re-armed from that moment.

No call waits for a chunk in flight. The servlet holds the reservation lock for the whole chunk it receives, which on a slow network is many seconds; pause(UploadReservation, int, String, boolean), resume(UploadReservation, int, boolean), cancel(UploadReservation, int, String) and markConsumed(UploadFileEntry, boolean) therefore try the lock, and when it is taken leave their change to the thread that holds it (UploadReservation.defer(Runnable)) and return. A pause by the App is told to the listeners and the client before it returns, and a pause or cancellation makes the servlet give up the chunk it is receiving: that chunk is answered as paused or gone, its bytes are dropped, and the file continues later from the offset the client was last acknowledged.

Author:
Christopher Mindus
  • Field Details

    • REASON_DISABLED

      public static final String REASON_DISABLED
      Refusal reason: uploads are disabled.
      See Also:
    • REASON_QUOTA

      public static final String REASON_QUOTA
      Refusal reason: the quota (session or server) is exhausted.
      See Also:
    • REASON_TOO_MANY

      public static final String REASON_TOO_MANY
      Refusal reason: too many files in the reservation.
      See Also:
    • REASON_TOO_LARGE

      public static final String REASON_TOO_LARGE
      Refusal reason: the declared size exceeds the per-file maximum.
      See Also:
    • REASON_TYPE

      public static final String REASON_TYPE
      Refusal reason: the declared type is not permitted.
      See Also:
    • REASON_START

      public static final String REASON_START
      Cancellation reason: the start timeout expired.
      See Also:
    • REASON_IDLE

      public static final String REASON_IDLE
      Cancellation reason: the idle timeout expired.
      See Also:
    • REASON_MAX_PAUSE

      public static final String REASON_MAX_PAUSE
      Cancellation reason: paused longer than the maximum pause time.
      See Also:
  • Constructor Details

  • Method Details

    • newReservationID

      public String newReservationID()
      Mints a reservation ID: 16 random bytes as 32 lower-case hex characters.
      Returns:
      The ID.
    • newSecret

      public String newSecret()
      Mints a per-file secret: 32 random bytes as base64url without padding.
      Returns:
      The secret.
    • sha256Hex

      public static String sha256Hex(String s)
      SHA-256 of a string, lower-case hex.
      Parameters:
      s - The string.
      Returns:
      The hash.
    • checkSecret

      public static boolean checkSecret(UploadFileEntry file, String presented)
      Checks a presented secret against a file's secret in constant time.
      Parameters:
      file - The file.
      presented - The header value, may be null.
      Returns:
      true only if the file has a secret and the presented value equals it.
    • usedServerBytes

      public long usedServerBytes()
      Sums the bytes held by every live reservation.
      Returns:
      The server-wide held bytes.
    • usedSessionBytes

      public long usedSessionBytes(String sessionKey)
      Sums the bytes held by the live reservations of one application session.
      Parameters:
      sessionKey - The application session UUID.
      Returns:
      The session's held bytes.
    • reserve

      public UploadReservation reserve(String sessionKey, String userUUID, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, boolean enabled) throws IOException
      Mints a reservation with the automatic failure notice on; see the seven-argument form.
      Parameters:
      sessionKey - The application session UUID (the quota key).
      userUUID - The user UUID for the sidecar, metadata only, may be empty.
      requests - The files, at least one.
      limits - The limits in force.
      listener - The per-reservation listener, or null.
      enabled - Whether new reservations are allowed at all.
      Returns:
      The reservation, accepted or refused.
      Throws:
      IOException - If the staging directory cannot be created.
    • reserve

      public UploadReservation reserve(String sessionKey, String userUUID, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, boolean enabled, boolean notices) throws IOException
      Mints a reservation. Files are accepted in order until one no longer fits the quota; the rest are refused individually. The reservation as a whole is refused when the count exceeds the maximum, in which case no file is accepted and nothing is written to disk.
      Parameters:
      sessionKey - The application session UUID (the quota key).
      userUUID - The user UUID for the sidecar, metadata only, may be empty.
      requests - The files, at least one.
      limits - The limits in force.
      listener - The per-reservation listener, or null.
      enabled - Whether new reservations are allowed at all.
      notices - Whether the App wants the automatic failure notice for this reservation.
      Returns:
      The reservation, accepted or refused.
      Throws:
      IOException - If the staging directory cannot be created.
    • refused

      public UploadReservation refused(String sessionKey, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, String reason)
      Builds a reservation refused as a whole for a reason outside the quota rules (e.g. the staging directory could not be created). Nothing is registered, nothing is on disk, no secret exists.
      Parameters:
      sessionKey - The application session UUID.
      requests - The files.
      limits - The limits in force.
      listener - The per-reservation listener, or null.
      reason - The refusal reason.
      Returns:
      The refused reservation.
    • get

      public UploadReservation get(String rid)
      Gets a live reservation.
      Parameters:
      rid - The reservation ID.
      Returns:
      The reservation, or null.
    • reservationsOf

      public List<UploadReservation> reservationsOf(String sessionKey)
      Gets the live reservations of one application session.
      Parameters:
      sessionKey - The application session UUID.
      Returns:
      The reservations.
    • size

      public int size()
      Gets the number of live reservations.
      Returns:
      The count.
    • acceptChunk

      public boolean acceptChunk(UploadReservation r, UploadFileEntry f, long compressedLen, long plainLen, boolean last, String remoteAddress) throws IOException
      Records an accepted chunk; the caller holds the reservation lock and has already appended and forced the bytes to the part. Rewrites the file sidecar, then the reservation's lastActivity, clears the start timeout and re-arms the idle timeout.
      Parameters:
      r - The reservation.
      f - The file.
      compressedLen - The bytes appended to the part.
      plainLen - The uncompressed bytes the chunk carried.
      last - Whether this was the final chunk.
      remoteAddress - The client's remote address, may be null.
      Returns:
      true if this chunk came from a different address than the first one AND that has not been reported before (log one WARNING, never block, never again).
      Throws:
      IOException - For sidecar write errors.
      IllegalStateException - If the lock is not held or the file is not receiving.
    • markVerified

      public void markVerified(UploadFileEntry f, String vtype, long measured) throws IOException
      Marks a complete file verified and closes the reservation when every file is final; the caller holds the lock.
      Parameters:
      f - The file.
      vtype - The verified type, empty if not sniffable.
      measured - The measured uncompressed size.
      Throws:
      IOException - For sidecar write errors.
    • markVerified

      public void markVerified(UploadFileEntry f, String vtype, long measured, boolean close) throws IOException
      Marks a complete file verified; the caller holds the lock.

      With close false the reservation is NOT closed here, even when this file was the last to become final: the caller posts the file's completion to its listeners first and then calls closeWhenFinal(UploadReservation), so that a listener hears every file's completion before the reservation's end (IIZI-SRV-UPLOAD-CLOSED-BEFORE-COMPLETED).

      Parameters:
      f - The file.
      vtype - The verified type, empty if not sniffable.
      measured - The measured uncompressed size.
      close - true to close the reservation when every file is final, false to leave that to closeWhenFinal(UploadReservation).
      Throws:
      IOException - For sidecar write errors.
    • closeWhenFinal

      public void closeWhenFinal(UploadReservation r)
      Closes the reservation when every file is final, and drops it when every file is terminal: the second half of markVerified(UploadFileEntry,String,long,boolean) with close false, for a caller that had something to post in between. The caller holds the lock. Harmless when nothing is final or the close has been done.
      Parameters:
      r - The reservation.
    • markFailed

      public void markFailed(UploadFileEntry f, String reason) throws IOException
      Marks a file failed and deletes its bytes; the caller holds the lock.
      Parameters:
      f - The file.
      reason - The reason.
      Throws:
      IOException - For file errors.
    • markConsumed

      public void markConsumed(UploadFileEntry f, boolean delete) throws IOException
      Marks a verified file consumed: taken by the App (bytes kept, never swept) or deleted by it.

      The call does not wait for a chunk in flight: when the lock is taken, the file is recorded as consumed (and deleted, if asked) by the thread that holds it.

      Parameters:
      f - The file.
      delete - Whether to delete the part now.
      Throws:
      IOException - For file errors.
    • pause

      public void pause(UploadReservation r, int fileIndex, String reason, boolean byApp)
      Pauses one file or every receiving file, by the App or by the client. A file that is not receiving and not already paused is left alone. A file already paused by the other party gets this party's flag as well, silently. Then the reservation's timers follow: paused as a whole means the start and idle timeouts are cancelled and the maximum pause timeout armed.

      The call does not wait for a chunk in flight. When the lock is taken, the pause is left to the thread that holds it; the App's pause is then told at once (FileUploadRegistry.IEvents.onFilePaused(UploadReservation, UploadFileEntry, String, boolean), which reaches the listeners and the client), and the servlet gives up the chunk it is receiving and answers it as paused.

      Parameters:
      r - The reservation.
      fileIndex - The file index, or -1 for every file.
      reason - The reason, never null.
      byApp - true for the App's pause (the servlet then refuses chunks), false for the client's notice (a chunk from it is its resume).
    • resume

      public void resume(UploadReservation r, int fileIndex, boolean byApp)
      Resumes one file or every file this party holds paused. The file receives again only when the other party does not hold it too; the App's release of a file the client still holds is reported through FileUploadRegistry.IEvents.onAppReleased(UploadReservation, UploadFileEntry) so the client learns it. Then the reservation's timers follow: receiving again means the idle timeout (or the start timeout, if no chunk has ever arrived) is re-armed from now.

      The call does not wait for a chunk in flight: when the lock is taken, the resume is left to the thread that holds it, after every change that was left to it before.

      Parameters:
      r - The reservation.
      fileIndex - The file index, or -1 for every file.
      byApp - true for the App's resume, false for the client's notice or chunk.
    • cancel

      public void cancel(UploadReservation r, int fileIndex, String reason)
      Cancels one file or the whole reservation: bytes deleted immediately, quota released.

      The call does not wait for a chunk in flight: when the lock is taken, the cancellation is left to the thread that holds it, and the servlet gives up the chunk it is receiving and answers it as gone.

      Parameters:
      r - The reservation.
      fileIndex - The file index, or -1 for all.
      reason - The reason.
    • cancelAll

      public void cancelAll(String reason)
      Cancels every live reservation (application session or server going away).
      Parameters:
      reason - The reason.
    • cancelSession

      public void cancelSession(String sessionKey, String reason)
      Cancels every live reservation of one application session.
      Parameters:
      sessionKey - The application session UUID.
      reason - The reason.
    • sweepIdle

      public int sweepIdle(long now)
      Belt and braces beside the timers: cancels every open reservation whose last activity is older than its idle timeout, or, while it is paused as a whole, whose pause is older than its maximum pause time.
      Parameters:
      now - The current time.
      Returns:
      The number cancelled.