@@ -293,7 +293,7 @@ static JsonSchema compile(JsonValue schemaJson, Options options, CompileOptions
293293 Objects .requireNonNull (schemaJson , "schemaJson" );
294294 Objects .requireNonNull (options , "options" );
295295 Objects .requireNonNull (compileOptions , "compileOptions" );
296- LOG .info (() -> "compile: Starting schema compilation with initial URI: " + java .net .URI .create ("urn:inmemory:root" ));
296+ LOG .fine (() -> "compile: Starting schema compilation with initial URI: " + java .net .URI .create ("urn:inmemory:root" ));
297297 LOG .fine (() -> "compile: Starting schema compilation with full options, schema type: " + schemaJson .getClass ().getSimpleName () +
298298 ", options.assertFormats=" + options .assertFormats () + ", compileOptions.remoteFetcher=" + compileOptions .remoteFetcher ().getClass ().getSimpleName ());
299299 LOG .fine (() -> "compile: fetch policy allowedSchemes=" + compileOptions .fetchPolicy ().allowedSchemes ());
@@ -354,7 +354,7 @@ static JsonSchema compile(JsonValue schemaJson, Options options, CompileOptions
354354 }
355355 }
356356
357- LOG .info (() -> "compile: Completed schema compilation, total roots compiled: " + rootCount );
357+ LOG .fine (() -> "compile: Completed schema compilation, total roots compiled: " + rootCount );
358358 LOG .fine (() -> "compile: Completed schema compilation with full options, result type: " + updatedResult .getClass ().getSimpleName ());
359359 return updatedResult ;
360360 }
@@ -446,7 +446,7 @@ static CompiledRegistry compileWorkStack(JsonValue initialJson,
446446 LOG .finest (() -> "compileWorkStack: added URI to active set, active now=" + active );
447447 try {
448448 // Fetch document if needed
449- JsonValue documentJson = fetchIfNeeded (currentUri , initialUri , initialJson , context );
449+ JsonValue documentJson = fetchIfNeeded (currentUri , initialUri , initialJson , context , compileOptions );
450450 LOG .finer (() -> "compileWorkStack: fetched document for URI: " + currentUri + ", json type: " + documentJson .getClass ().getSimpleName ());
451451 LOG .finest (() -> "compileWorkStack: fetched documentJson object=" + documentJson + ", type=" + documentJson .getClass ().getSimpleName () + ", content=" + documentJson );
452452
@@ -475,7 +475,11 @@ static CompiledRegistry compileWorkStack(JsonValue initialJson,
475475 }
476476
477477 /// Fetch document if needed (primary vs remote)
478- static JsonValue fetchIfNeeded (java .net .URI docUri , java .net .URI initialUri , JsonValue initialJson , ResolverContext context ) {
478+ static JsonValue fetchIfNeeded (java .net .URI docUri ,
479+ java .net .URI initialUri ,
480+ JsonValue initialJson ,
481+ ResolverContext context ,
482+ CompileOptions compileOptions ) {
479483 LOG .fine (() -> "fetchIfNeeded: docUri=" + docUri + ", initialUri=" + initialUri );
480484 LOG .finest (() -> "fetchIfNeeded: docUri object=" + docUri + ", scheme=" + docUri .getScheme () + ", host=" + docUri .getHost () + ", path=" + docUri .getPath ());
481485 LOG .finest (() -> "fetchIfNeeded: initialUri object=" + initialUri + ", scheme=" + initialUri .getScheme () + ", host=" + initialUri .getHost () + ", path=" + initialUri .getPath ());
@@ -488,7 +492,7 @@ static JsonValue fetchIfNeeded(java.net.URI docUri, java.net.URI initialUri, Jso
488492 return initialJson ;
489493 }
490494
491- // MVF: Fetch remote document using RemoteFetcher from context
495+ // MVF: Fetch remote document using RemoteFetcher from compile options
492496 LOG .finer (() -> "fetchIfNeeded: fetching remote document: " + docUri );
493497 try {
494498 // Get the base URI without fragment for document fetching
@@ -499,10 +503,40 @@ static JsonValue fetchIfNeeded(java.net.URI docUri, java.net.URI initialUri, Jso
499503
500504 LOG .finest (() -> "fetchIfNeeded: document URI without fragment: " + docUriWithoutFragment );
501505
502- // Use RemoteFetcher from context - for now we need to get it from compile options
503- // Since we don't have direct access to compile options in this method, we'll use a basic HTTP fetcher
504- // This is a temporary implementation that should be replaced with proper context integration
505- RemoteFetcher .FetchResult fetchResult = fetchRemoteDocument (docUriWithoutFragment );
506+ // Enforce allowed schemes
507+ String scheme = docUriWithoutFragment .getScheme ();
508+ if (scheme == null || !compileOptions .fetchPolicy ().allowedSchemes ().contains (scheme )) {
509+ throw new RemoteResolutionException (
510+ docUriWithoutFragment ,
511+ RemoteResolutionException .Reason .POLICY_DENIED ,
512+ "Scheme not allowed by policy: " + scheme
513+ );
514+ }
515+
516+ // Prefer a local file mapping for tests when using file:// URIs
517+ java .net .URI fetchUri = docUriWithoutFragment ;
518+ if ("file" .equalsIgnoreCase (scheme )) {
519+ String base = System .getProperty ("json.schema.test.resources" , "src/test/resources" );
520+ String path = fetchUri .getPath ();
521+ if (path != null && path .startsWith ("/" )) path = path .substring (1 );
522+ java .nio .file .Path abs = java .nio .file .Paths .get (base , path ).toAbsolutePath ();
523+ java .net .URI alt = abs .toUri ();
524+ fetchUri = alt ;
525+ LOG .fine (() -> "fetchIfNeeded: Using file mapping for fetch: " + alt + " (original=" + docUriWithoutFragment + ")" );
526+ }
527+
528+ // Fetch via provided RemoteFetcher to ensure consistent policy/normalization
529+ RemoteFetcher .FetchResult fetchResult ;
530+ try {
531+ fetchResult = compileOptions .remoteFetcher ().fetch (fetchUri , compileOptions .fetchPolicy ());
532+ } catch (RemoteResolutionException e1 ) {
533+ // On mapping miss, retry original URI once
534+ if (!fetchUri .equals (docUriWithoutFragment )) {
535+ fetchResult = compileOptions .remoteFetcher ().fetch (docUriWithoutFragment , compileOptions .fetchPolicy ());
536+ } else {
537+ throw e1 ;
538+ }
539+ }
506540 JsonValue fetchedDocument = fetchResult .document ();
507541
508542 LOG .fine (() -> "fetchIfNeeded: successfully fetched remote document: " + docUriWithoutFragment + ", document type: " + fetchedDocument .getClass ().getSimpleName ());
@@ -516,71 +550,7 @@ static JsonValue fetchIfNeeded(java.net.URI docUri, java.net.URI initialUri, Jso
516550 }
517551 }
518552
519- /// Temporary remote document fetcher - should be integrated with proper context
520- private static RemoteFetcher .FetchResult fetchRemoteDocument (java .net .URI uri ) {
521- LOG .finest (() -> "fetchRemoteDocument: fetching URI: " + uri );
522-
523- try {
524- java .net .URL url = uri .toURL ();
525- java .net .URLConnection connection = url .openConnection ();
526-
527- // Handle different URL schemes
528- if ("file" .equals (uri .getScheme ())) {
529- // File URLs - local filesystem access
530- LOG .finest (() -> "fetchRemoteDocument: handling file:// URL" );
531- try (java .io .BufferedReader reader = new java .io .BufferedReader (
532- new java .io .InputStreamReader (connection .getInputStream (), java .nio .charset .StandardCharsets .UTF_8 ))) {
533- StringBuilder content = new StringBuilder ();
534- String line ;
535- while ((line = reader .readLine ()) != null ) {
536- content .append (line ).append ("\n " );
537- }
538-
539- String jsonContent = content .toString ().trim ();
540- JsonValue document = Json .parse (jsonContent );
541- long byteSize = jsonContent .getBytes (java .nio .charset .StandardCharsets .UTF_8 ).length ;
542-
543- return new RemoteFetcher .FetchResult (document , byteSize , Optional .empty ());
544- }
545- } else if ("http" .equals (uri .getScheme ()) || "https" .equals (uri .getScheme ())) {
546- // HTTP URLs - use HttpURLConnection
547- LOG .finest (() -> "fetchRemoteDocument: handling HTTP/HTTPS URL" );
548- java .net .HttpURLConnection httpConnection = (java .net .HttpURLConnection ) connection ;
549- httpConnection .setRequestMethod ("GET" );
550- httpConnection .setConnectTimeout (5000 ); // 5 seconds
551- httpConnection .setReadTimeout (5000 ); // 5 seconds
552-
553- int responseCode = httpConnection .getResponseCode ();
554- if (responseCode != java .net .HttpURLConnection .HTTP_OK ) {
555- throw new RemoteResolutionException (uri , RemoteResolutionException .Reason .NETWORK_ERROR ,
556- "HTTP request failed with status: " + responseCode );
557- }
558-
559- try (java .io .BufferedReader reader = new java .io .BufferedReader (
560- new java .io .InputStreamReader (httpConnection .getInputStream (), java .nio .charset .StandardCharsets .UTF_8 ))) {
561- StringBuilder content = new StringBuilder ();
562- String line ;
563- while ((line = reader .readLine ()) != null ) {
564- content .append (line ).append ("\n " );
565- }
566-
567- String jsonContent = content .toString ().trim ();
568- JsonValue document = Json .parse (jsonContent );
569- long byteSize = jsonContent .getBytes (java .nio .charset .StandardCharsets .UTF_8 ).length ;
570-
571- LOG .finest (() -> "fetchRemoteDocument: successfully fetched " + byteSize + " bytes from " + uri );
572- return new RemoteFetcher .FetchResult (document , byteSize , Optional .empty ());
573- }
574- } else {
575- // Unsupported scheme
576- throw new RemoteResolutionException (uri , RemoteResolutionException .Reason .POLICY_DENIED ,
577- "Unsupported URI scheme: " + uri .getScheme () + ". Only file://, http://, and https:// are supported." );
578- }
579- } catch (java .io .IOException e ) {
580- throw new RemoteResolutionException (uri , RemoteResolutionException .Reason .NETWORK_ERROR ,
581- "IO error while fetching remote document" , e );
582- }
583- }
553+
584554
585555 /// Build root schema for a document
586556 static JsonSchema buildRoot (JsonValue documentJson ,
0 commit comments