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