package Breccia.Web.imager; import Java.Async; import Java.UnsourcedInterrupt; import java.net.URI; import java.nio.file.Path; import java.util.Set; import static Breccia.Web.imager.Imageability.indeterminate; import static Breccia.Web.imager.Project.looksBrecciaLike; import static Java.Collections.forEachRemaining; import static java.lang.Thread.sleep; import static Java.URI_References.isRemote; import static Java.URIs.isHTTP; /** A crawling probe of the formal resources at a remote Web host. It reads the timestamps * of each resource and so determines the imageability of the local source files that depend * on it (dependants). Assigned a particular host from among `formalResources`, the probe * determines as imageable any dependant of a resource whose image file does not postdate * the resource, then updates the dependant’s `imageabilityDeterminations` accordingly. * * @see ImageMould#formalResources * @see ImageMould#imageabilityDeterminations */ final class RemoteChangeProbe implements Runnable { /** @param host The identifier of the network host whose resources to probe, * concordant with `{@linkplain ImageMould#host(java.net.URI) ImageMould.host}`. * @param mould The mould to use. */ RemoteChangeProbe( final String host, final ImageMould mould ) { this.host = host; this.mould = mould; } /** Appends to `b` the causal part of a user report that `ref` is improbeable. * * @param ref A URI reference to a remote resource. * @see java.lang.Throwable#getCause() * @see #looksProbeable(URI) */ static void appendImprobeableCause( final URI ref, final StringBuilder b ) { b.append( "Unable to access the referent by this form of reference. Consider marking it " ); if( !looksBrecciaLike( ref )) b.append( "non-fractal or " ); b.append( "private." ); } /** Whether it appears possible to access the referent of the given reference. * * @param ref A URI reference to a remote resource. * @see * URI generic syntax §4.1, URI reference * @see Java.URI_References#isRemote() */ static boolean looksProbeable( final URI ref ) { if( !isRemote( ref )) throw new IllegalArgumentException(); boolean answer = true; if( ref.isOpaque() ) answer = false; else { final String scheme = ref.getScheme(); if( scheme != null ) { if( ref.getHost() == null ) answer = false; /* Trouble with no justifying use case. Such a hostless URI is allowed a rootless path, making it hard to resolve from outside the network context (e.g. HTTP) implied by the scheme. */ else if( !isHTTP( scheme )) answer = false; } else answer = false; } /* Trouble with no justifying use case, a network-path reference. https://www.rfc-editor.org/rfc/rfc3986#section-4.2 */ return answer; } /** The delay in milliseconds before each successive HTTP query to a Web host. */ static final int msQueryInterval = /*TEST*/0; /* Cf. `Crawl-delay`. https://en.wikipedia.org/wiki/Robots_exclusion_standard#Crawl-delay_directive */ // ━━━ R u n n a b l e ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ /** {@inheritDoc} Call once only. This method is thread safe for a single call. */ public @Async @Override void run() { final var rr = mould.formalResources.remote.entrySet().iterator(); forEachRemaining( rr, (resource, dependants) -> { if( !host.equals( resource.getHost() )) return; probe( resource, dependants ); if( rr.hasNext() ) { try { sleep( msQueryInterval ); } catch( final InterruptedException x ) { Thread.currentThread().interrupt(); // Avoid hiding the fact of interruption. throw new UnsourcedInterrupt( x ); }}}); } //// P r i v a t e //////////////////////////////////////////////////////////////////////////////////// private final String host; private final ImageMould mould; private void probe( final URI ref, final Set dependants ) { if( !looksProbeable( ref )) throw new IllegalArgumentException(); // Ensure the resource still needs probing // ─────────────────────────────────────── boolean toProbe = false; for( final Path dep: dependants ) { final ImageabilityReference depImageability = mould.imageabilityDeterminations.get( dep ); if( depImageability.get() == indeterminate ) { toProbe = true; break; }} if( !toProbe ) return; // Probe the resource /* ────────────────── Deferred, as per `http://reluk.ca/project/Breccia/Web/imager/notes.brec.xht#deferral,hTTP,fetches`. */; }} // Copyright © 2020-2022, 2025 Michael Allan. Licence MIT.