3434import org .eclipse .aether .DefaultRepositorySystemSession ;
3535import org .eclipse .aether .RepositorySystem ;
3636import org .eclipse .aether .RepositorySystemSession ;
37- import org .eclipse .aether .connector .basic .BasicRepositoryConnectorFactory ;
3837import org .eclipse .aether .impl .DefaultServiceLocator ;
3938import org .eclipse .aether .repository .LocalRepository ;
4039import org .eclipse .aether .repository .RepositoryPolicy ;
4140import org .eclipse .aether .spi .connector .RepositoryConnectorFactory ;
4241import org .eclipse .aether .spi .connector .transport .TransporterFactory ;
43- import org .eclipse .aether .transport .file .FileTransporterFactory ;
44- import org .eclipse .aether .transport .http .HttpTransporterFactory ;
4542
43+ import org .springframework .lang .Nullable ;
4644import org .springframework .util .StringUtils ;
4745
46+ /**
47+ * Utilities for creating/obtaining Aether (Maven Resolver) components.
48+ *
49+ * <p>
50+ * <b>Key changes vs the old version:</b>
51+ * <ul>
52+ * <li>No hard reference to {@code BasicRepositoryConnectorFactory} or transporter
53+ * implementations. We register them reflectively if present, otherwise we rely on Maven's
54+ * injected components or whatever the runtime provides via ServiceLoader.</li>
55+ * <li>Supports using Maven-injected {@link RepositorySystem} and
56+ * {@link RepositorySystemSession} when called from a Mojo, avoiding classpath issues
57+ * entirely.</li>
58+ * </ul>
59+ */
4860final class AetherFactories {
4961
5062 private static final Log log = LogFactory .getLog (AetherFactories .class );
@@ -61,21 +73,126 @@ private AetherFactories() {
6173 throw new IllegalStateException ("Can't instantiate a utility class" );
6274 }
6375
64- public static RepositorySystem newRepositorySystem () {
76+ /**
77+ * Return the injected system if available, otherwise create a new one via a
78+ * {@link DefaultServiceLocator} without hard-linking to optional providers.
79+ */
80+ static RepositorySystem repositorySystemOr (@ Nullable RepositorySystem injectedOrNull ) {
81+ if (injectedOrNull != null ) {
82+ if (log .isDebugEnabled ()) {
83+ log .debug ("Using Maven-injected RepositorySystem" );
84+ }
85+ return injectedOrNull ;
86+ }
87+ if (log .isDebugEnabled ()) {
88+ log .debug ("No injected RepositorySystem provided; creating one via ServiceLocator" );
89+ }
90+ return newRepositorySystemFallback ();
91+ }
92+
93+ /**
94+ * Return the injected session if available, otherwise create a new session for the
95+ * given system.
96+ */
97+ static RepositorySystemSession sessionOr (RepositorySystem system , @ Nullable RepositorySystemSession injectedOrNull ,
98+ boolean workOffline ) {
99+ if (injectedOrNull != null ) {
100+ if (log .isDebugEnabled ()) {
101+ log .debug ("Using Maven-injected RepositorySystemSession (workOffline=" + injectedOrNull .isOffline ()
102+ + ")" );
103+ }
104+ return injectedOrNull ;
105+ }
106+ return newSession (system , workOffline );
107+ }
108+
109+ /**
110+ * Fallback creation using a ServiceLocator. This tries to register common providers
111+ * reflectively:
112+ * <ul>
113+ * <li>org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory</li>
114+ * <li>org.eclipse.aether.transport.file.FileTransporterFactory</li>
115+ * <li>org.eclipse.aether.transport.http.HttpTransporterFactory</li>
116+ * </ul>
117+ * If any of these are missing on the classpath, we simply don't register them and let
118+ * ServiceLoader discover whatever is available. This avoids
119+ * {@code NoClassDefFoundError} at class load time.
120+ */
121+ private static RepositorySystem newRepositorySystemFallback () {
65122 DefaultServiceLocator locator = MavenRepositorySystemUtils .newServiceLocator ();
66- locator .addService (RepositoryConnectorFactory .class , BasicRepositoryConnectorFactory .class );
67- locator .addService (TransporterFactory .class , FileTransporterFactory .class );
68- locator .addService (TransporterFactory .class , HttpTransporterFactory .class );
69- return locator .getService (RepositorySystem .class );
123+
124+ // Helpful diagnostics
125+ locator .setErrorHandler (new DefaultServiceLocator .ErrorHandler () {
126+ @ Override
127+ public void serviceCreationFailed (Class <?> type , Class <?> impl , Throwable exception ) {
128+ if (log .isDebugEnabled ()) {
129+ log .debug ("Failed to create service " + type .getName () + " via "
130+ + (impl != null ? impl .getName () : "<null>" ) + ": " + exception .toString ());
131+ }
132+ }
133+ });
134+
135+ // Try to register connector + transporters reflectively, but do not hard-link
136+ // them.
137+ registerIfPresent (locator , "org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory" ,
138+ RepositoryConnectorFactory .class );
139+
140+ registerIfPresent (locator , "org.eclipse.aether.transport.file.FileTransporterFactory" ,
141+ TransporterFactory .class );
142+
143+ registerIfPresent (locator , "org.eclipse.aether.transport.http.HttpTransporterFanewRepositorySystemctory" ,
144+ TransporterFactory .class );
145+
146+ RepositorySystem system = locator .getService (RepositorySystem .class );
147+
148+ // If system still ended up null, give a helpful hint.
149+ if (system == null ) {
150+ throw new IllegalStateException ("Failed to obtain RepositorySystem. "
151+ + "Ensure Maven Resolver is on the classpath and, when running inside a Maven plugin, "
152+ + "prefer using the Maven-injected RepositorySystem/RepositorySystemSession." );
153+ }
154+ return system ;
70155 }
71156
72- public static RepositorySystemSession newSession (RepositorySystem system , boolean workOffline ) {
157+ @ SuppressWarnings ({ "unchecked" , "rawtypes" })
158+ private static void registerIfPresent (DefaultServiceLocator locator , String implClassName , Class <?> serviceType ) {
159+ try {
160+ ClassLoader cl = Thread .currentThread ().getContextClassLoader ();
161+ if (cl == null ) {
162+ cl = AetherFactories .class .getClassLoader ();
163+ }
164+ Class <?> impl = Class .forName (implClassName , false , cl );
165+ if (!serviceType .isAssignableFrom (impl )) {
166+ if (log .isDebugEnabled ()) {
167+ log .debug ("Class " + implClassName + " is not assignable to " + serviceType .getName ());
168+ }
169+ return ;
170+ }
171+ locator .addService ((Class ) serviceType , (Class ) impl );
172+ if (log .isDebugEnabled ()) {
173+ log .debug ("Registered " + implClassName + " as " + serviceType .getSimpleName ());
174+ }
175+ }
176+ catch (ClassNotFoundException ex ) {
177+ // Silently skip; not on classpath (e.g., plugin didn't ship connector-basic).
178+ if (log .isDebugEnabled ()) {
179+ log .debug ("Optional provider not found on classpath: " + implClassName );
180+ }
181+ }
182+ }
183+
184+ /**
185+ * Create a new {@link RepositorySystemSession}, controlling offline/update/checksum
186+ * policies.
187+ */
188+ static RepositorySystemSession newSession (RepositorySystem system , boolean workOffline ) {
73189 DefaultRepositorySystemSession session = MavenRepositorySystemUtils .newSession ();
74190 session .setOffline (workOffline );
75191 if (!workOffline ) {
76192 session .setUpdatePolicy (RepositoryPolicy .UPDATE_POLICY_ALWAYS );
77193 }
78194 session .setChecksumPolicy (RepositoryPolicy .CHECKSUM_POLICY_WARN );
195+
79196 String localRepositoryDirectory = localRepositoryDirectory (workOffline );
80197 if (log .isDebugEnabled ()) {
81198 log .debug ("Local Repository Directory set to [" + localRepositoryDirectory + "]. Work offline: ["
@@ -86,7 +203,11 @@ public static RepositorySystemSession newSession(RepositorySystem system, boolea
86203 return session ;
87204 }
88205
89- protected static String localRepositoryDirectory (boolean workOffline ) {
206+ /**
207+ * Determine local repo directory: respect settings/system prop; use temp when online
208+ * to avoid pollution.
209+ */
210+ static String localRepositoryDirectory (boolean workOffline ) {
90211 String localRepoLocationFromSettings = settings ().getLocalRepository ();
91212 String currentLocalRepo = readPropertyFromSystemProps (localRepoLocationFromSettings );
92213 if (workOffline ) {
@@ -101,17 +222,17 @@ private static String temporaryDirectory() {
101222 }
102223 catch (IOException e ) {
103224 if (log .isDebugEnabled ()) {
104- log .debug ("Failed to create a new temporary directory, will generate a new one under temp dir" );
225+ log .debug ("Failed to create a new temporary directory, will generate a new one under temp dir" , e );
105226 }
106227 return System .getProperty ("java.io.tmpdir" ) + File .separator + RANDOM .nextInt ();
107228 }
108229 }
109230
110- private static String readPropertyFromSystemProps (String localRepoLocationFromSettings ) {
231+ private static String readPropertyFromSystemProps (@ Nullable String localRepoLocationFromSettings ) {
111232 String mavenLocalRepo = fromSystemPropOrEnv (MAVEN_LOCAL_REPOSITORY_LOCATION );
112233 return StringUtils .hasText (mavenLocalRepo ) ? mavenLocalRepo
113- : localRepoLocationFromSettings != null ? localRepoLocationFromSettings
114- : System .getProperty ("user.home" ) + File .separator + ".m2" + File .separator + "repository" ;
234+ : ( localRepoLocationFromSettings != null ? localRepoLocationFromSettings
235+ : System .getProperty ("user.home" ) + File .separator + ".m2" + File .separator + "repository" ) ;
115236 }
116237
117238 // system prop takes precedence over env var
@@ -139,22 +260,21 @@ private static File userSettings() {
139260 return new File (user );
140261 }
141262
142- protected static Settings settings () {
263+ static Settings settings () {
143264 SettingsBuilder builder = new DefaultSettingsBuilderFactory ().newInstance ();
144265 SettingsBuildingRequest request = new DefaultSettingsBuildingRequest ();
145266 request .setUserSettingsFile (userSettings ());
146267 String global = fromSystemPropOrEnv (MAVEN_GLOBAL_SETTINGS_LOCATION );
147268 if (global != null ) {
148269 request .setGlobalSettingsFile (new File (global ));
149270 }
150- SettingsBuildingResult result ;
151271 try {
152- result = builder .build (request );
272+ SettingsBuildingResult result = builder .build (request );
273+ return result .getEffectiveSettings ();
153274 }
154275 catch (SettingsBuildingException ex ) {
155276 throw new IllegalStateException (ex );
156277 }
157- return result .getEffectiveSettings ();
158278 }
159279
160280}
0 commit comments