99import java .io .StringWriter ;
1010import java .io .Writer ;
1111import java .net .HttpURLConnection ;
12+ import java .net .MalformedURLException ;
1213import java .nio .charset .Charset ;
1314import java .nio .charset .StandardCharsets ;
15+ import java .net .URL ;
1416import java .util .List ;
1517import java .util .Map ;
1618
1719import org .apache .commons .io .ByteOrderMark ;
1820import org .apache .commons .io .IOUtils ;
1921import org .apache .commons .io .input .BOMInputStream ;
22+ import org .apache .http .Header ;
2023import org .apache .http .client .methods .CloseableHttpResponse ;
2124import org .apache .http .client .methods .HttpGet ;
2225import org .apache .http .client .methods .HttpUriRequest ;
@@ -344,18 +347,7 @@ public static Object fromURL(java.net.URL url, CloseableHttpClient httpClient)
344347 // Accept headers as it's likely to be file: or jar:
345348 in = url .openStream ();
346349 } else {
347- final HttpUriRequest request = new HttpGet (url .toExternalForm ());
348- // We prefer application/ld+json, but fallback to
349- // application/json
350- // or whatever is available
351- request .addHeader ("Accept" , ACCEPT_HEADER );
352-
353- response = httpClient .execute (request );
354- final int status = response .getStatusLine ().getStatusCode ();
355- if (status != 200 && status != 203 ) {
356- throw new IOException ("Can't retrieve " + url + ", status code: " + status );
357- }
358- in = response .getEntity ().getContent ();
350+ in = getJsonLdViaHttpUri (url , httpClient , response );
359351 }
360352 return fromInputStream (in );
361353 } finally {
@@ -371,6 +363,56 @@ public static Object fromURL(java.net.URL url, CloseableHttpClient httpClient)
371363 }
372364 }
373365
366+ private static InputStream getJsonLdViaHttpUri (final URL url , final CloseableHttpClient httpClient ,
367+ CloseableHttpResponse response ) throws IOException {
368+ final HttpUriRequest request = new HttpGet (url .toExternalForm ());
369+ // We prefer application/ld+json, but fallback to application/json
370+ // or whatever is available
371+ request .addHeader ("Accept" , ACCEPT_HEADER );
372+ response = httpClient .execute (request );
373+
374+ final int status = response .getStatusLine ().getStatusCode ();
375+ if (status != 200 && status != 203 ) {
376+ throw new IOException ("Can't retrieve " + url + ", status code: " + status );
377+ }
378+ // follow alternate document location
379+ // https://www.w3.org/TR/json-ld11/#alternate-document-location
380+ URL alternateLink = alternateLink (url , response );
381+ if (alternateLink != null ) {
382+ return getJsonLdViaHttpUri (alternateLink , httpClient , response );
383+ }
384+ return response .getEntity ().getContent ();
385+ }
386+
387+ private static URL alternateLink (URL url , CloseableHttpResponse response )
388+ throws MalformedURLException , IOException {
389+ if (response .getEntity ().getContentLength () > 0
390+ && !response .getEntity ().getContentType ().getValue ().equals ("application/ld+json" )) {
391+ for (Header header : response .getAllHeaders ()) {
392+ if (header .getName ().equalsIgnoreCase ("link" )) {
393+ String alternateLink = "" ;
394+ boolean relAlternate = false ;
395+ boolean jsonld = false ;
396+ for (String value : header .getValue ().split (";" )) {
397+ if (value .trim ().startsWith ("<" )) {
398+ alternateLink = value .replaceAll ("<(.*)>" , "$1" );
399+ }
400+ if (value .trim ().startsWith ("type=\" application/ld+json\" " )) {
401+ jsonld = true ;
402+ }
403+ if (value .trim ().startsWith ("rel=\" alternate\" " )) {
404+ relAlternate = true ;
405+ }
406+ }
407+ if (jsonld && relAlternate && !alternateLink .isEmpty ()) {
408+ return new URL (url .getProtocol () + "://" + url .getAuthority () + alternateLink );
409+ }
410+ }
411+ }
412+ }
413+ return null ;
414+ }
415+
374416 /**
375417 * Fallback method directly using the {@link java.net.HttpURLConnection}
376418 * class for cases where servers do not interoperate correctly with Apache
@@ -384,7 +426,7 @@ public static Object fromURL(java.net.URL url, CloseableHttpClient httpClient)
384426 * @throws IOException
385427 * If there was an IO error during parsing.
386428 */
387- public static Object fromURLJavaNet (java . net . URL url ) throws JsonParseException , IOException {
429+ public static Object fromURLJavaNet (URL url ) throws JsonParseException , IOException {
388430 final HttpURLConnection urlConn = (HttpURLConnection ) url .openConnection ();
389431 urlConn .addRequestProperty ("Accept" , ACCEPT_HEADER );
390432
0 commit comments