2222
2323package de .splatgames .aether .datafixers .cli .bootstrap ;
2424
25+ import com .google .common .base .Preconditions ;
2526import de .splatgames .aether .datafixers .api .bootstrap .DataFixerBootstrap ;
2627import org .jetbrains .annotations .NotNull ;
2728
2829import java .lang .reflect .Constructor ;
2930import java .util .ServiceLoader ;
3031
3132/**
32- * Loads {@link DataFixerBootstrap} implementations.
33+ * Utility class for loading {@link DataFixerBootstrap} implementations at runtime.
34+ *
35+ * <p>This class provides multiple methods for discovering and instantiating
36+ * bootstrap implementations:</p>
3337 *
3438 * <h2>Discovery Methods</h2>
3539 * <ol>
36- * <li>Explicit class name via {@code --bootstrap} option</li>
37- * <li>ServiceLoader discovery via {@code META-INF/services}</li>
40+ * <li><b>Explicit class name</b> via {@link #load(String)} - Used when the CLI
41+ * receives the {@code --bootstrap} option with a fully qualified class name</li>
42+ * <li><b>ServiceLoader discovery</b> via {@link #discover()} or {@link #loadFirst()} -
43+ * Automatically discovers implementations registered in
44+ * {@code META-INF/services/de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap}</li>
3845 * </ol>
3946 *
47+ * <h2>Thread Safety</h2>
48+ * <p>All methods are thread-safe as they do not maintain any mutable state.</p>
49+ *
50+ * <h2>Example Usage</h2>
51+ * <pre>{@code
52+ * // Load by explicit class name
53+ * DataFixerBootstrap bootstrap = BootstrapLoader.load("com.example.MyBootstrap");
54+ *
55+ * // Auto-discover via ServiceLoader
56+ * DataFixerBootstrap bootstrap = BootstrapLoader.loadFirst();
57+ *
58+ * // Iterate over all discovered bootstraps
59+ * for (DataFixerBootstrap bootstrap : BootstrapLoader.discover()) {
60+ * System.out.println("Found: " + bootstrap.getClass().getName());
61+ * }
62+ * }</pre>
63+ *
4064 * @author Erik Pfoertner
65+ * @see DataFixerBootstrap
66+ * @see BootstrapLoadException
67+ * @see java.util.ServiceLoader
4168 * @since 0.3.0
4269 */
4370public final class BootstrapLoader {
4471
72+ /**
73+ * Private constructor to prevent instantiation.
74+ *
75+ * <p>This is a utility class with only static methods and should not be instantiated.</p>
76+ */
4577 private BootstrapLoader () {
4678 // Utility class
4779 }
4880
4981 /**
50- * Loads a bootstrap by its fully qualified class name.
82+ * Loads a bootstrap implementation by its fully qualified class name.
5183 *
52- * <p>The class must:</p>
84+ * <p>This method uses reflection to load and instantiate the specified class.
85+ * The class must satisfy the following requirements:</p>
5386 * <ul>
54- * <li>Implement {@link DataFixerBootstrap}</li>
55- * <li>Have a public no-argument constructor</li>
87+ * <li>Must implement {@link DataFixerBootstrap}</li>
88+ * <li>Must have a public no-argument constructor</li>
89+ * <li>Must be accessible on the current thread's context class loader</li>
5690 * </ul>
5791 *
58- * @param className the fully qualified class name
59- * @return the bootstrap instance
60- * @throws BootstrapLoadException if loading fails
92+ * <h3>Error Handling</h3>
93+ * <p>All reflection-related exceptions are wrapped in {@link BootstrapLoadException}
94+ * with descriptive error messages to aid debugging:</p>
95+ * <ul>
96+ * <li>{@code ClassNotFoundException} - Class not found on classpath</li>
97+ * <li>{@code NoSuchMethodException} - Missing public no-arg constructor</li>
98+ * <li>{@code ReflectiveOperationException} - Instantiation failure</li>
99+ * </ul>
100+ *
101+ * @param className the fully qualified class name (e.g., "com.example.MyBootstrap"),
102+ * must not be {@code null}
103+ * @return the instantiated bootstrap instance, never {@code null}
104+ * @throws BootstrapLoadException if the class cannot be found, does not implement
105+ * DataFixerBootstrap, lacks a no-arg constructor,
106+ * or instantiation fails
107+ * @see #discover()
108+ * @see #loadFirst()
61109 */
62110 @ NotNull
63111 public static DataFixerBootstrap load (@ NotNull final String className ) {
112+ Preconditions .checkNotNull (className , "className must not be null" );
113+
64114 try {
65115 final Class <?> clazz = Class .forName (className );
66116
@@ -87,20 +137,52 @@ public static DataFixerBootstrap load(@NotNull final String className) {
87137 }
88138
89139 /**
90- * Discovers bootstraps via ServiceLoader.
140+ * Discovers all bootstrap implementations via Java's {@link ServiceLoader} mechanism.
141+ *
142+ * <p>This method searches for implementations registered in:</p>
143+ * <pre>
144+ * META-INF/services/de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap
145+ * </pre>
91146 *
92- * @return iterable of discovered bootstraps
147+ * <p>The returned iterable is lazy - implementations are instantiated on demand
148+ * as the iterator is consumed. Each call to this method creates a fresh
149+ * ServiceLoader instance.</p>
150+ *
151+ * <h3>Registration</h3>
152+ * <p>To register a bootstrap for discovery, create a file at:</p>
153+ * <pre>
154+ * src/main/resources/META-INF/services/de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap
155+ * </pre>
156+ * <p>containing the fully qualified class name of your implementation.</p>
157+ *
158+ * @return an iterable over all discovered bootstrap implementations;
159+ * may be empty if none are registered
160+ * @see ServiceLoader#load(Class)
161+ * @see #loadFirst()
162+ * @see #load(String)
93163 */
94164 @ NotNull
95165 public static Iterable <DataFixerBootstrap > discover () {
96166 return ServiceLoader .load (DataFixerBootstrap .class );
97167 }
98168
99169 /**
100- * Loads the first available bootstrap from ServiceLoader.
170+ * Loads the first available bootstrap from the ServiceLoader.
171+ *
172+ * <p>This is a convenience method that returns the first bootstrap found
173+ * via {@link #discover()}. Useful when only one bootstrap is expected to
174+ * be registered in the application.</p>
175+ *
176+ * <p>The order in which bootstraps are discovered is not guaranteed and
177+ * depends on the classpath order. If multiple bootstraps are registered,
178+ * consider using {@link #discover()} to iterate over all of them or
179+ * {@link #load(String)} to specify an exact implementation.</p>
101180 *
102- * @return the first bootstrap found
103- * @throws BootstrapLoadException if no bootstrap is found
181+ * @return the first discovered bootstrap implementation, never {@code null}
182+ * @throws BootstrapLoadException if no bootstrap implementations are found
183+ * via ServiceLoader
184+ * @see #discover()
185+ * @see #load(String)
104186 */
105187 @ NotNull
106188 public static DataFixerBootstrap loadFirst () {
0 commit comments