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.