[java-shib-attribute] branch main updated: JSATTR-14 - Registry loader constructor breaks on jar-based classpaths

Codeberg noreply at shibboleth.net
Thu Jul 16 14:23:17 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-shib-attribute.

View the commit online:
https://codeberg.org/Shibboleth/java-shib-attribute/commit/4fd36f9e21ae01595c314c5c5a2913d393120c21

The following commit(s) were added to refs/heads/main by this push:
     new 4fd36f9e2 JSATTR-14 - Registry loader constructor breaks on jar-based classpaths
4fd36f9e2 is described below

commit 4fd36f9e21ae01595c314c5c5a2913d393120c21
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Thu Jul 16 10:23:01 2026 -0400

    JSATTR-14 - Registry loader constructor breaks on jar-based classpaths
    
    https://shibboleth.atlassian.net/browse/JSATTR-14
    
    Add new constructors and fail fast flag to control behavior better.
---
 .../transcoding/impl/TranscodingRuleLoader.java    | 197 +++++++++++++++++----
 1 file changed, 161 insertions(+), 36 deletions(-)

diff --git a/shib-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java b/shib-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java
index f9c423fc3..323c9a7f0 100644
--- a/shib-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java
+++ b/shib-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java
@@ -18,6 +18,7 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
+import java.nio.file.NotDirectoryException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -29,6 +30,7 @@ import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
 
 import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.shared.annotation.ParameterName;
@@ -51,9 +53,12 @@ public class TranscodingRuleLoader {
     
     /** Rules loaded. */
     @Nonnull private final Collection<TranscodingRule> rules;
+
+    /** Whether to rethrow exceptions during construction or populate empty. */
+    private final boolean failOnErrors;
     
     /**
-     * Load rules from all files found below a directory root.
+     * Load rules from all files found below a directory root expressed as a Spring resource.
      * 
      * <p>Files are assumed to be Java property files in text format.</p>
      * 
@@ -62,49 +67,65 @@ public class TranscodingRuleLoader {
      * <p>The file extensions must include the period, and only apply to
      * files, while all directories will be examined.</p>
      * 
-     * @param dir root to search
+     * @param resource root to search
      * @param extensions file extensions to include
+     * @param failFast true iff I/O failures should be surfaced
      * 
      * @throws IOException if an error occurs
+     * 
+     * @since 5.3.0
      */
-    public TranscodingRuleLoader(@Nonnull @ParameterName(name="dir") final Path dir,
-            @Nullable @ParameterName(name="extensions") final Collection<String> extensions)
-                    throws IOException {
-        log.debug("Loading rules from directory ({})", dir);
-        final Collection<TranscodingRule> holder = new ArrayList<>();
+    public TranscodingRuleLoader(@Nonnull @ParameterName(name="resource") final Resource resource,
+            @Nullable @ParameterName(name="extensions") final Collection<String> extensions,
+            @ParameterName(name="failFast") final boolean failFast) throws IOException {
         
-        try (final DirectoryStream<Path> dirstream  = Files.newDirectoryStream(dir)) {
-            for (final Path child : dirstream) {
-                final File file =  child.toFile();
-                if (file.isDirectory()) {
-                    try {
-                        holder.addAll(new TranscodingRuleLoader(child, extensions).getRules());
-                    } catch (final IOException e) {
-                        log.error("Failed to load rules from directory ({})", file, e);
-                    }
-                } else if (extensions == null || extensions.isEmpty() ||
-                        PredicateSupport.anyMatch((String ext) -> file.getName().endsWith(ext)).test(extensions)) {
-                    log.debug("Loading rule from property set in file ({})", file);
-                    try {
-                        final TranscodingRule rule =
-                                TranscodingRule.fromResource(ResourceHelper.of(new FileSystemResource(file)));
-                        if (rule.getMap().isEmpty()) {
-                            log.info("Transcoding file {} contained no rules", child);
-                        } else {
-                            holder.add(rule);
-                        }
-                    } catch (final IOException e) {
-                        log.error("Failed to load rule from file ({})", file, e);
-                    }
-                } else {
-                    log.debug("Ignoring file ({}) with non-matching extension", file);
-                }
+        failOnErrors = failFast;
+        
+        final Path path;
+        try {
+            path = resource.getFilePath();
+        } catch (final Exception e) {
+            log.error("Exception accessing resource ({}) as directory", resource, e);
+            if (failOnErrors) {
+                throw e;
             }
+            rules = CollectionSupport.emptyList();
+            return;
         }
-        
-        rules = CollectionSupport.copyToList(holder);
+        rules = load(path, extensions);
     }
 
+    /**
+     * Equivalent to {@link #TranscodingRuleLoader(Resource, Collection, boolean)}
+     * with true fail fast behavior.
+     * 
+     * @param resource root to search
+     * @param extensions file extensions to include
+     * 
+     * @throws IOException if an error occurs
+     * 
+     * @since 5.3.0
+     */
+    public TranscodingRuleLoader(@Nonnull @ParameterName(name="resource") final Resource resource,
+            @Nullable @ParameterName(name="extensions") final Collection<String> extensions) throws IOException {
+        this(resource, extensions, true);
+    }
+    
+    /**
+     * Equivalent to {@link #TranscodingRuleLoader(Resource, Collection, boolean)}
+     * with null extensions and true fail fast behavior.
+     * 
+     * @param resource root to search
+     * 
+     * @throws IOException if an error occurs
+     * 
+     * @since 5.3.0
+     */
+    public TranscodingRuleLoader(@Nonnull @ParameterName(name="resource") final Resource resource)
+                    throws IOException {
+        this(resource, null, true);
+    }    
+    
     /**
      * Load rules from all files found below a directory root.
      * 
@@ -112,12 +133,50 @@ public class TranscodingRuleLoader {
      * 
      * <p>Individual rules that fail to load will be skipped.</p>
      * 
+     * <p>The file extensions must include the period, and only apply to
+     * files, while all directories will be examined.</p>
+     * 
+     * @param dir root to search
+     * @param extensions file extensions to include
+     * @param failFast true iff I/O failures should be surfaced
+     * 
+     * @throws IOException if an error occurs
+     * 
+     * @since 5.3.0
+     */
+    public TranscodingRuleLoader(@Nonnull @ParameterName(name="dir") final Path dir,
+            @Nullable @ParameterName(name="extensions") final Collection<String> extensions,
+            @ParameterName(name="failFast") final boolean failFast)
+                    throws IOException {
+        
+        failOnErrors = failFast;
+        rules = load(dir, extensions);
+    }
+
+    /**
+     * Equivalent to {@link #TranscodingRuleLoader(Path, Collection, boolean)}
+     * with null extensions and true fail fast behavior.
+     * 
+     * @param dir root to search
+     * @param extensions file extensions to include
+     * 
+     * @throws IOException if an error occurs
+     */
+    public TranscodingRuleLoader(@Nonnull @ParameterName(name="dir") final Path dir,
+            @Nullable @ParameterName(name="extensions") final Collection<String> extensions) throws IOException {
+        this(dir, extensions, true);
+    }
+    
+    /**
+     * Equivalent to {@link #TranscodingRuleLoader(Path, Collection, boolean)}
+     * with null extensions and true fail fast behavior.
+     * 
      * @param dir root to search
      * 
      * @throws IOException if an error occurs
      */
     public TranscodingRuleLoader(@Nonnull @ParameterName(name="dir") final Path dir) throws IOException {
-        this(dir, null);
+        this(dir, null, true);
     }
     
     /**
@@ -128,6 +187,9 @@ public class TranscodingRuleLoader {
     public TranscodingRuleLoader(@Nonnull @ParameterName(name="maps") final Collection<Map<String,Object>> maps) {
         Constraint.isNotNull(maps, "Input collection cannot be null");
         
+        // Irrelevant.
+        failOnErrors = true;
+        
         rules = maps
                 .stream()
                 .map(m -> {
@@ -137,6 +199,69 @@ public class TranscodingRuleLoader {
                 .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList()))
                 .get();
     }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Performs the load step using the supplied parameters from the various constructors.
+     * 
+     * @param dir path to attempt load from
+     * @param extensions file extensions to include
+     * 
+     * @return the loaded rules
+     * 
+     * @throws IOException on failure
+     * 
+     * @since 5.3.0
+     */
+    @Nonnull @Unmodifiable @NotLive private Collection<TranscodingRule> load(
+            @Nonnull @ParameterName(name="dir") final Path dir,
+            @Nullable @ParameterName(name="extensions") final Collection<String> extensions) throws IOException {
+
+        log.debug("Loading rules from directory ({})", dir);
+        final Collection<TranscodingRule> holder = new ArrayList<>();
+        
+        try (final DirectoryStream<Path> dirstream  = Files.newDirectoryStream(dir)) {
+            for (final Path child : dirstream) {
+                final File file =  child.toFile();
+                if (file.isDirectory()) {
+                    try {
+                        holder.addAll(new TranscodingRuleLoader(child, extensions, failOnErrors).getRules());
+                    } catch (final IOException e) {
+                        log.error("Failed to load rules from directory ({})", file, e);
+                    }
+                } else if (extensions == null || extensions.isEmpty() ||
+                        PredicateSupport.anyMatch((String ext) -> file.getName().endsWith(ext)).test(extensions)) {
+                    log.debug("Loading rule from property set in file ({})", file);
+                    try {
+                        final TranscodingRule rule =
+                                TranscodingRule.fromResource(ResourceHelper.of(new FileSystemResource(file)));
+                        if (rule.getMap().isEmpty()) {
+                            log.info("Transcoding file {} contained no rules", child);
+                        } else {
+                            holder.add(rule);
+                        }
+                    } catch (final IOException e) {
+                        log.error("Failed to load rule from file ({})", file, e);
+                    }
+                } else {
+                    log.debug("Ignoring file ({}) with non-matching extension", file);
+                }
+            }
+        } catch (final NotDirectoryException e) {
+            log.error("TranscodingRuleLoader path {{}} was not a directory", dir, e);
+            if (failOnErrors) {
+                throw e;
+            }
+        } catch (final IOException e) {
+            log.error("Exception accessing path {{}}", dir, e);
+            if (failOnErrors) {
+                throw e;
+            }
+        }
+        
+        return CollectionSupport.copyToList(holder);
+    }
+// Checkstyle: CyclomaticComplexity ON
 
     /**
      * Get the rules loaded by this object.

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list