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.impl;
20  
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Objects;
25  import java.util.Optional;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.stream.Stream;
28  
29  import org.apache.maven.api.Project;
30  import org.apache.maven.api.Session;
31  import org.apache.maven.api.SessionData;
32  import org.apache.maven.api.Toolchain;
33  import org.apache.maven.api.annotations.Nonnull;
34  import org.apache.maven.api.annotations.Nullable;
35  import org.apache.maven.api.di.Inject;
36  import org.apache.maven.api.di.Named;
37  import org.apache.maven.api.di.Singleton;
38  import org.apache.maven.api.services.Lookup;
39  import org.apache.maven.api.services.ToolchainFactory;
40  import org.apache.maven.api.services.ToolchainFactoryException;
41  import org.apache.maven.api.services.ToolchainManager;
42  import org.apache.maven.api.services.ToolchainManagerException;
43  import org.apache.maven.api.toolchain.ToolchainModel;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  @Named
48  @Singleton
49  public class DefaultToolchainManager implements ToolchainManager {
50      private final Map<String, ToolchainFactory> factories;
51      private final Logger logger;
52  
53      @Inject
54      public DefaultToolchainManager(Map<String, ToolchainFactory> factories) {
55          this(factories, null);
56      }
57  
58      /**
59       * Used for tests only
60       */
61      protected DefaultToolchainManager(Map<String, ToolchainFactory> factories, Logger logger) {
62          this.factories = factories;
63          this.logger = logger != null ? logger : LoggerFactory.getLogger(DefaultToolchainManager.class);
64      }
65  
66      @Nonnull
67      @Override
68      public List<Toolchain> getToolchains(
69              @Nonnull Session session, @Nonnull String type, @Nullable Map<String, String> requirements)
70              throws ToolchainManagerException {
71          ToolchainFactory factory = factories.get(Objects.requireNonNull(type, "type"));
72          if (factory == null) {
73              logger.error("Missing toolchain factory for type: " + type + ". Possibly caused by misconfigured project.");
74              return List.of();
75          }
76          return Stream.concat(
77                          session.getToolchains().stream()
78                                  .filter(model -> Objects.equals(type, model.getType()))
79                                  .map(this::createToolchain)
80                                  .flatMap(Optional::stream),
81                          factory.createDefaultToolchain().stream())
82                  .filter(toolchain -> requirements == null || toolchain.matchesRequirements(requirements))
83                  .toList();
84      }
85  
86      @Nonnull
87      @Override
88      public Optional<Toolchain> getToolchainFromBuildContext(@Nonnull Session session, @Nonnull String type)
89              throws ToolchainManagerException {
90          Map<String, Object> context = retrieveContext(session);
91          ToolchainModel model = (ToolchainModel) context.get("toolchain-" + type);
92          return Optional.ofNullable(model).flatMap(this::createToolchain);
93      }
94  
95      @Override
96      public void storeToolchainToBuildContext(@Nonnull Session session, @Nonnull Toolchain toolchain) {
97          Map<String, Object> context = retrieveContext(session);
98          context.put("toolchain-" + toolchain.getType(), toolchain.getModel());
99      }
100 
101     private Optional<Toolchain> createToolchain(ToolchainModel model) {
102         String type = Objects.requireNonNull(model.getType(), "model.getType()");
103         ToolchainFactory factory = factories.get(type);
104         if (factory != null) {
105             try {
106                 return Optional.of(factory.createToolchain(model));
107             } catch (ToolchainFactoryException e) {
108                 throw new ToolchainManagerException("Error creating toolchain of type " + type, e);
109             }
110         } else {
111             logger.error("Missing toolchain factory for type: " + type + ". Possibly caused by misconfigured project.");
112         }
113         return Optional.empty();
114     }
115 
116     private static final SessionData.Key<ConcurrentHashMap<Project, ConcurrentHashMap<String, Object>>>
117             TOOLCHAIN_CONTEXT_KEY = (SessionData.Key) SessionData.key(ConcurrentHashMap.class, "toolchain-context");
118 
119     protected Map<String, Object> retrieveContext(Session session) {
120         Optional<Project> current = session.getService(Lookup.class).lookupOptional(Project.class);
121         if (current.isPresent()) {
122             var map = session.getData().computeIfAbsent(TOOLCHAIN_CONTEXT_KEY, ConcurrentHashMap::new);
123             return map.computeIfAbsent(current.get(), p -> new ConcurrentHashMap<>());
124         }
125         return new HashMap<>();
126     }
127 }