View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.impl.standalone;
20  
21  import java.nio.file.Path;
22  import java.nio.file.Paths;
23  import java.time.Instant;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Objects;
30  import java.util.Optional;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.stream.Collectors;
33  
34  import org.apache.maven.api.Artifact;
35  import org.apache.maven.api.Lifecycle;
36  import org.apache.maven.api.Packaging;
37  import org.apache.maven.api.ProducedArtifact;
38  import org.apache.maven.api.Project;
39  import org.apache.maven.api.RemoteRepository;
40  import org.apache.maven.api.Session;
41  import org.apache.maven.api.Type;
42  import org.apache.maven.api.Version;
43  import org.apache.maven.api.di.Provides;
44  import org.apache.maven.api.di.SessionScoped;
45  import org.apache.maven.api.model.PluginContainer;
46  import org.apache.maven.api.model.Profile;
47  import org.apache.maven.api.services.ArtifactManager;
48  import org.apache.maven.api.services.LifecycleRegistry;
49  import org.apache.maven.api.services.Lookup;
50  import org.apache.maven.api.services.MavenException;
51  import org.apache.maven.api.services.PackagingRegistry;
52  import org.apache.maven.api.services.RepositoryFactory;
53  import org.apache.maven.api.services.SettingsBuilder;
54  import org.apache.maven.api.services.TypeRegistry;
55  import org.apache.maven.api.settings.Settings;
56  import org.apache.maven.api.spi.TypeProvider;
57  import org.apache.maven.di.Injector;
58  import org.apache.maven.di.Key;
59  import org.apache.maven.di.impl.DIException;
60  import org.apache.maven.internal.impl.AbstractSession;
61  import org.apache.maven.internal.impl.InternalSession;
62  import org.apache.maven.internal.impl.di.SessionScope;
63  import org.apache.maven.internal.impl.resolver.scopes.Maven4ScopeManagerConfiguration;
64  import org.eclipse.aether.DefaultRepositorySystemSession;
65  import org.eclipse.aether.RepositorySystem;
66  import org.eclipse.aether.RepositorySystemSession;
67  import org.eclipse.aether.impl.RemoteRepositoryManager;
68  import org.eclipse.aether.internal.impl.scope.ScopeManagerImpl;
69  import org.eclipse.aether.repository.LocalRepository;
70  import org.eclipse.aether.repository.LocalRepositoryManager;
71  
72  public class ApiRunner {
73  
74      /**
75       * Create a new session.
76       */
77      public static Session createSession() {
78          Injector injector = Injector.create();
79          injector.bindInstance(Injector.class, injector);
80          injector.bindImplicit(ApiRunner.class);
81          injector.discover(ApiRunner.class.getClassLoader());
82          Session session = injector.getInstance(Session.class);
83          SessionScope scope = new SessionScope();
84          scope.enter();
85          scope.seed(Session.class, session);
86          injector.bindScope(SessionScoped.class, scope);
87          return session;
88      }
89  
90      static class DefaultSession extends AbstractSession {
91  
92          private final Map<String, String> systemProperties;
93          private final Instant startTime = Instant.now();
94  
95          DefaultSession(RepositorySystemSession session, RepositorySystem repositorySystem, Lookup lookup) {
96              this(session, repositorySystem, Collections.emptyList(), null, lookup);
97          }
98  
99          protected DefaultSession(
100                 RepositorySystemSession session,
101                 RepositorySystem repositorySystem,
102                 List<RemoteRepository> repositories,
103                 List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
104                 Lookup lookup) {
105             super(session, repositorySystem, repositories, resolverRepositories, lookup);
106             systemProperties = System.getenv().entrySet().stream()
107                     .collect(Collectors.toMap(e -> "env." + e.getKey(), Map.Entry::getValue));
108             System.getProperties().forEach((k, v) -> systemProperties.put(k.toString(), v.toString()));
109         }
110 
111         @Override
112         protected Session newSession(RepositorySystemSession session, List<RemoteRepository> repositories) {
113             return new DefaultSession(session, repositorySystem, repositories, null, lookup);
114         }
115 
116         @Override
117         public Settings getSettings() {
118             return Settings.newInstance();
119         }
120 
121         @Override
122         public Map<String, String> getUserProperties() {
123             return Map.of();
124         }
125 
126         @Override
127         public Map<String, String> getSystemProperties() {
128             return systemProperties;
129         }
130 
131         @Override
132         public Map<String, String> getEffectiveProperties(Project project) {
133             HashMap<String, String> result = new HashMap<>(getSystemProperties());
134             if (project != null) {
135                 result.putAll(project.getModel().getProperties());
136             }
137             result.putAll(getUserProperties());
138             return result;
139         }
140 
141         @Override
142         public Version getMavenVersion() {
143             return null;
144         }
145 
146         @Override
147         public int getDegreeOfConcurrency() {
148             return 0;
149         }
150 
151         @Override
152         public Instant getStartTime() {
153             return startTime;
154         }
155 
156         @Override
157         public Path getTopDirectory() {
158             return null;
159         }
160 
161         @Override
162         public Path getRootDirectory() {
163             throw new IllegalStateException();
164         }
165 
166         @Override
167         public List<Project> getProjects() {
168             return List.of();
169         }
170 
171         @Override
172         public Map<String, Object> getPluginContext(Project project) {
173             throw new UnsupportedInStandaloneModeException();
174         }
175     }
176 
177     @Provides
178     @SuppressWarnings("unused")
179     static Lookup newLookup(Injector injector) {
180         return new Lookup() {
181             @Override
182             public <T> T lookup(Class<T> type) {
183                 try {
184                     return injector.getInstance(type);
185                 } catch (DIException e) {
186                     throw new MavenException("Unable to locate instance of type " + type, e);
187                 }
188             }
189 
190             @Override
191             public <T> T lookup(Class<T> type, String name) {
192                 try {
193                     return injector.getInstance(Key.of(type, name));
194                 } catch (DIException e) {
195                     throw new MavenException("Unable to locate instance of type " + type, e);
196                 }
197             }
198 
199             @Override
200             public <T> Optional<T> lookupOptional(Class<T> type) {
201                 try {
202                     return Optional.of(injector.getInstance(type));
203                 } catch (DIException e) {
204                     return Optional.empty();
205                 }
206             }
207 
208             @Override
209             public <T> Optional<T> lookupOptional(Class<T> type, String name) {
210                 try {
211                     return Optional.of(injector.getInstance(Key.of(type, name)));
212                 } catch (DIException e) {
213                     return Optional.empty();
214                 }
215             }
216 
217             @Override
218             public <T> List<T> lookupList(Class<T> type) {
219                 return injector.getInstance(new Key<List<T>>() {});
220             }
221 
222             @Override
223             public <T> Map<String, T> lookupMap(Class<T> type) {
224                 return injector.getInstance(new Key<Map<String, T>>() {});
225             }
226         };
227     }
228 
229     @Provides
230     @SuppressWarnings("unused")
231     static ArtifactManager newArtifactManager() {
232         return new ArtifactManager() {
233             private final Map<Artifact, Path> paths = new ConcurrentHashMap<>();
234 
235             @Override
236             public Optional<Path> getPath(Artifact artifact) {
237                 return Optional.ofNullable(paths.get(artifact));
238             }
239 
240             @Override
241             public void setPath(ProducedArtifact artifact, Path path) {
242                 paths.put(artifact, path);
243             }
244         };
245     }
246 
247     @Provides
248     @SuppressWarnings("unused")
249     static PackagingRegistry newPackagingRegistry(TypeRegistry typeRegistry) {
250         return id -> Optional.of(new DumbPackaging(id, typeRegistry.require(id), Map.of()));
251     }
252 
253     @Provides
254     @SuppressWarnings("unused")
255     static TypeRegistry newTypeRegistry(List<TypeProvider> providers) {
256         return new TypeRegistry() {
257             @Override
258             public Optional<Type> lookup(String id) {
259                 return providers.stream()
260                         .flatMap(p -> p.provides().stream())
261                         .filter(t -> Objects.equals(id, t.id()))
262                         .findAny();
263             }
264         };
265     }
266 
267     @Provides
268     @SuppressWarnings("unused")
269     static LifecycleRegistry newLifecycleRegistry() {
270         return new LifecycleRegistry() {
271 
272             @Override
273             public Iterator<Lifecycle> iterator() {
274                 return Collections.emptyIterator();
275             }
276 
277             @Override
278             public Optional<Lifecycle> lookup(String id) {
279                 return Optional.empty();
280             }
281 
282             @Override
283             public List<String> computePhases(Lifecycle lifecycle) {
284                 return List.of();
285             }
286         };
287     }
288 
289     @Provides
290     @SuppressWarnings("unused")
291     static RepositorySystemSupplier newRepositorySystemSupplier() {
292         return new RepositorySystemSupplier();
293     }
294 
295     @Provides
296     @SuppressWarnings("unused")
297     static RepositorySystem newRepositorySystem(RepositorySystemSupplier repositorySystemSupplier) {
298         return repositorySystemSupplier.getRepositorySystem();
299     }
300 
301     @Provides
302     @SuppressWarnings("unused")
303     static RemoteRepositoryManager newRemoteRepositoryManager(RepositorySystemSupplier repositorySystemSupplier) {
304         return repositorySystemSupplier.getRemoteRepositoryManager();
305     }
306 
307     @Provides
308     @SuppressWarnings("unused")
309     static Session newSession(RepositorySystem system, Lookup lookup) {
310         Map<String, String> properties = new HashMap<>();
311         // Env variables prefixed with "env."
312         System.getenv().forEach((k, v) -> properties.put("env." + k, v));
313         // Java System properties
314         System.getProperties().forEach((k, v) -> properties.put(k.toString(), v.toString()));
315 
316         // SettingsDecrypter settingsDecrypter =
317         // (SettingsDecrypter)Objects.requireNonNull(this.createSettingsDecrypter(preBoot));
318         //        new DefaultProfileSelector(List.of(
319         //                new JdkVersionProfileActivator(),
320         //                new PropertyProfileActivator(),
321         //                new OperatingSystemProfileActivator(),
322         //                new FileProfileActivator(new ProfileActivationFilePathInterpolator(
323         //                        new DefaultPathTranslator(), new DefaultRootLocator()))));
324 
325         Path userHome = Paths.get(properties.get("user.home"));
326         Path mavenUserHome = userHome.resolve(".m2");
327         Path mavenSystemHome = properties.containsKey("maven.home")
328                 ? Paths.get(properties.get("maven.home"))
329                 : properties.containsKey("env.MAVEN_HOME") ? Paths.get(properties.get("env.MAVEN_HOME")) : null;
330 
331         DefaultRepositorySystemSession rsession = new DefaultRepositorySystemSession(h -> false);
332         rsession.setScopeManager(new ScopeManagerImpl(Maven4ScopeManagerConfiguration.INSTANCE));
333         rsession.setSystemProperties(properties);
334         rsession.setConfigProperties(properties);
335 
336         DefaultSession session = new DefaultSession(
337                 rsession,
338                 system,
339                 List.of(lookup.lookup(RepositoryFactory.class)
340                         .createRemote("central", "https://repo.maven.apache.org/maven2")),
341                 null,
342                 lookup);
343 
344         Settings settings = session.getService(SettingsBuilder.class)
345                 .build(
346                         session,
347                         mavenSystemHome != null ? mavenSystemHome.resolve("settings.xml") : null,
348                         mavenUserHome.resolve("settings.xml"))
349                 .getEffectiveSettings();
350 
351         settings.getProfiles();
352 
353         // local repository
354         String localRepository = settings.getLocalRepository() != null
355                         && !settings.getLocalRepository().isEmpty()
356                 ? settings.getLocalRepository()
357                 : mavenUserHome.resolve("repository").toString();
358         LocalRepositoryManager llm = system.newLocalRepositoryManager(rsession, new LocalRepository(localRepository));
359         rsession.setLocalRepositoryManager(llm);
360         // active proxies
361         // TODO
362         // active profiles
363 
364         DefaultSession defaultSession = new DefaultSession(
365                 rsession,
366                 system,
367                 List.of(lookup.lookup(RepositoryFactory.class)
368                         .createRemote("central", "https://repo.maven.apache.org/maven2")),
369                 null,
370                 lookup);
371 
372         Profile profile = session.getService(SettingsBuilder.class)
373                 .convert(org.apache.maven.api.settings.Profile.newBuilder()
374                         .repositories(settings.getRepositories())
375                         .pluginRepositories(settings.getPluginRepositories())
376                         .build());
377         RepositoryFactory repositoryFactory = session.getService(RepositoryFactory.class);
378         List<RemoteRepository> repositories = profile.getRepositories().stream()
379                 .map(repositoryFactory::createRemote)
380                 .toList();
381         InternalSession s = (InternalSession) session.withRemoteRepositories(repositories);
382         InternalSession.associate(rsession, s);
383         return s;
384 
385         // List<RemoteRepository> repositories = repositoryFactory.createRemote();
386 
387         //        session.getService(SettingsBuilder.class).convert()
388 
389         //        settings.getDelegate().getRepositories().stream()
390         //                        .map(r -> SettingsUtilsV4.)
391         //        defaultSession.getService(RepositoryFactory.class).createRemote()
392         //        return defaultSession;
393     }
394 
395     static class UnsupportedInStandaloneModeException extends MavenException {}
396 
397     record DumbPackaging(String id, Type type, Map<String, PluginContainer> plugins) implements Packaging {}
398 }