001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2017-2019 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.maven.cdi;
018
019import java.io.BufferedInputStream;
020import java.io.File;
021import java.io.IOException;
022import java.io.InputStream;
023
024import java.net.JarURLConnection;
025import java.net.URI;
026import java.net.URISyntaxException;
027import java.net.URL;
028
029import java.nio.file.Files;
030import java.nio.file.Path;
031import java.nio.file.Paths;
032
033import java.security.CodeSource;
034import java.security.ProtectionDomain;
035
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.HashMap;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Map;
042import java.util.Objects;
043import java.util.Properties;
044import java.util.Set;
045
046import java.util.jar.JarFile;
047
048import javax.enterprise.context.Dependent;
049
050import javax.enterprise.event.Observes;
051
052import javax.enterprise.inject.Any;
053import javax.enterprise.inject.Instance;
054import javax.enterprise.inject.Produces;
055import javax.enterprise.inject.Typed;
056
057import javax.enterprise.inject.literal.SingletonLiteral;
058
059import javax.enterprise.inject.spi.Bean;
060import javax.enterprise.inject.spi.BeanAttributes;
061import javax.enterprise.inject.spi.BeforeBeanDiscovery;
062import javax.enterprise.inject.spi.Extension;
063import javax.enterprise.inject.spi.ProcessBeanAttributes;
064import javax.inject.Named;
065import javax.inject.Singleton;
066
067import org.apache.maven.model.Model;
068
069import org.apache.maven.model.building.DefaultModelBuilder;
070import org.apache.maven.model.building.DefaultModelProcessor;
071import org.apache.maven.model.building.ModelBuildingRequest;
072import org.apache.maven.model.building.ModelProblemCollector;
073import org.apache.maven.model.building.ModelProcessor;
074
075import org.apache.maven.model.composition.DefaultDependencyManagementImporter;
076
077import org.apache.maven.model.inheritance.DefaultInheritanceAssembler;
078
079import org.apache.maven.model.interpolation.StringVisitorModelInterpolator;
080
081import org.apache.maven.model.io.DefaultModelReader;
082
083import org.apache.maven.model.locator.DefaultModelLocator;
084
085import org.apache.maven.model.management.DefaultDependencyManagementInjector;
086import org.apache.maven.model.management.DefaultPluginManagementInjector;
087
088import org.apache.maven.model.normalization.DefaultModelNormalizer;
089
090import org.apache.maven.model.path.DefaultModelPathTranslator;
091import org.apache.maven.model.path.DefaultModelUrlNormalizer;
092import org.apache.maven.model.path.DefaultPathTranslator;
093import org.apache.maven.model.path.DefaultUrlNormalizer;
094
095import org.apache.maven.model.plugin.DefaultPluginConfigurationExpander;
096import org.apache.maven.model.plugin.DefaultReportConfigurationExpander;
097import org.apache.maven.model.plugin.DefaultReportingConverter;
098import org.apache.maven.model.plugin.LifecycleBindingsInjector;
099
100import org.apache.maven.model.profile.DefaultProfileInjector;
101import org.apache.maven.model.profile.DefaultProfileSelector;
102import org.apache.maven.model.profile.ProfileSelector;
103
104import org.apache.maven.model.profile.activation.FileProfileActivator;
105import org.apache.maven.model.profile.activation.JdkVersionProfileActivator;
106import org.apache.maven.model.profile.activation.OperatingSystemProfileActivator;
107import org.apache.maven.model.profile.activation.ProfileActivator;
108import org.apache.maven.model.profile.activation.PropertyProfileActivator;
109
110import org.apache.maven.model.superpom.DefaultSuperPomProvider;
111
112import org.apache.maven.model.validation.DefaultModelValidator;
113
114import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
115import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
116import org.apache.maven.repository.internal.DefaultVersionResolver;
117import org.apache.maven.repository.internal.MavenRepositorySystemUtils; // for javadoc only
118import org.apache.maven.repository.internal.SnapshotMetadataGeneratorFactory;
119import org.apache.maven.repository.internal.VersionsMetadataGeneratorFactory;
120
121import org.apache.maven.settings.Mirror;
122import org.apache.maven.settings.Profile;
123import org.apache.maven.settings.Repository;
124import org.apache.maven.settings.Settings;
125
126import org.apache.maven.settings.building.DefaultSettingsBuilder;
127import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
128import org.apache.maven.settings.building.SettingsBuilder;
129import org.apache.maven.settings.building.SettingsBuildingException;
130import org.apache.maven.settings.building.SettingsBuildingResult;
131import org.apache.maven.settings.building.SettingsProblem;
132
133import org.apache.maven.settings.io.DefaultSettingsReader;
134import org.apache.maven.settings.io.DefaultSettingsWriter;
135
136import org.apache.maven.settings.merge.MavenSettingsMerger;
137
138import org.apache.maven.settings.validation.DefaultSettingsValidator;
139
140import org.eclipse.aether.DefaultRepositorySystemSession;
141import org.eclipse.aether.RepositoryListener;
142import org.eclipse.aether.RepositorySystem;
143import org.eclipse.aether.RepositorySystemSession;
144
145import org.eclipse.aether.artifact.ArtifactTypeRegistry;
146import org.eclipse.aether.artifact.DefaultArtifactType;
147
148import org.eclipse.aether.collection.DependencyGraphTransformer;
149import org.eclipse.aether.collection.DependencyManager;
150import org.eclipse.aether.collection.DependencySelector;
151import org.eclipse.aether.collection.DependencyTraverser;
152
153import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
154
155import org.eclipse.aether.impl.LocalRepositoryProvider;
156import org.eclipse.aether.impl.MetadataGeneratorFactory;
157
158import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
159import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider;
160import org.eclipse.aether.internal.impl.DefaultDeployer;
161import org.eclipse.aether.internal.impl.DefaultFileProcessor;
162import org.eclipse.aether.internal.impl.DefaultInstaller;
163import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider;
164import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
165import org.eclipse.aether.internal.impl.DefaultOfflineController;
166import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager;
167import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider;
168import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher;
169import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider;
170import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
171import org.eclipse.aether.internal.impl.DefaultSyncContextFactory;
172import org.eclipse.aether.internal.impl.DefaultTransporterProvider;
173import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
174import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
175import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
176import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
177import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
178
179import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector;
180
181import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
182
183import org.eclipse.aether.repository.LocalRepository;
184import org.eclipse.aether.repository.LocalRepositoryManager;
185import org.eclipse.aether.repository.MirrorSelector;
186import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
187import org.eclipse.aether.repository.RemoteRepository;
188
189import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
190
191import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
192
193import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
194
195import org.eclipse.aether.spi.connector.transport.TransporterFactory;
196
197import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
198
199import org.eclipse.aether.transfer.TransferListener;
200
201import org.eclipse.aether.transport.file.FileTransporterFactory;
202
203import org.eclipse.aether.transport.http.HttpTransporterFactory;
204
205import org.eclipse.aether.util.artifact.DefaultArtifactTypeRegistry;
206
207import org.eclipse.aether.util.graph.manager.ClassicDependencyManager;
208
209import org.eclipse.aether.util.graph.selector.AndDependencySelector;
210import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
211import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
212import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
213
214import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
215import org.eclipse.aether.util.graph.transformer.ConflictResolver;
216import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner;
217import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
218import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
219import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
220import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
221
222import org.eclipse.aether.util.graph.traverser.FatArtifactTraverser;
223
224import org.eclipse.aether.util.repository.DefaultMirrorSelector;
225import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
226
227import org.microbean.configuration.cdi.annotation.ConfigurationValue;
228
229import org.microbean.maven.cdi.annotation.Resolution;
230
231import org.slf4j.ILoggerFactory;
232import org.slf4j.LoggerFactory;
233
234/**
235 * A <a
236 * href="http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#spi">CDI
237 * 2.0 portable extension</a> that exposes the <a
238 * href="https://maven.apache.org/resolver/">Maven Artifact Resolver
239 * components</a> as CDI beans, particularly for the purposes of
240 * dependency resolution.
241 *
242 * @author <a href="http://about.me/lairdnelson"
243 * target="_parent">Laird Nelson</a>
244 *
245 * @see RepositorySystem
246 *
247 * @see RepositorySystemSession
248 */
249public class MavenExtension implements Extension {
250
251
252  /*
253   * Instance fields.
254   */
255
256
257  /**
258   * A {@link Map} of Maven artifact coordinates represented as {@link
259   * Properties} objects indexed by {@link URI} objects representing
260   * their location.
261   *
262   * <p>This field is never {@code null}.</p>
263   *
264   * <p>This field is not safe for concurrent use by multiple threads.
265   * Threads should synchronize on its value to coordinate access.</p>
266   *
267   * @see #getGroupArtifactVersionCoordinates(URL)
268   */
269  private final Map<URI, Properties> coordinates;
270
271
272  /*
273   * Constructors.
274   */
275
276
277  /**
278   * Creates a new {@link MavenExtension}.
279   */
280  public MavenExtension() {
281    super();
282    this.coordinates = new HashMap<>();
283  }
284
285
286  /*
287   * Instance methods.
288   */
289
290
291  /**
292   * Registers certain Maven Resolver components as CDI beans in
293   * appropriate scopes.
294   *
295   * @param event the {@link BeforeBeanDiscovery} event indicating
296   * that bean discovery has started; may be {@code null} in which
297   * case no action will be taken
298   *
299   * @see BeforeBeanDiscovery#addAnnotatedType(Class, String)
300   *
301   * @see <a
302   * href="https://github.com/apache/maven-resolver/blob/9417310df326cd4a58b0ef534e6c0e29f7b4cb47/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java#L100-L151">Guice
303   * bindings for {@code maven-resolver-impl}</a>
304   *
305   * @see <a
306   * href="https://github.com/apache/maven-resolver/blob/9417310df326cd4a58b0ef534e6c0e29f7b4cb47/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java#L186-L215">{@code
307   * DefaultSerivceLocator} bindings</a>
308   */
309  private final void beforeBeanDiscovery(@Observes final BeforeBeanDiscovery event) {
310    if (event != null) {
311
312      //
313      // Types effectively bound by DefaultServiceLocator
314      //
315
316      event.addAnnotatedType(DefaultArtifactResolver.class, "maven").add(SingletonLiteral.INSTANCE);
317      event.addAnnotatedType(DefaultChecksumPolicyProvider.class, "maven").add(SingletonLiteral.INSTANCE);
318      event.addAnnotatedType(DefaultDependencyCollector.class, "maven").add(SingletonLiteral.INSTANCE);
319      event.addAnnotatedType(DefaultDeployer.class, "maven").add(SingletonLiteral.INSTANCE);
320      event.addAnnotatedType(DefaultFileProcessor.class, "maven").add(SingletonLiteral.INSTANCE);
321      event.addAnnotatedType(DefaultInstaller.class, "maven").add(SingletonLiteral.INSTANCE);
322      event.addAnnotatedType(DefaultLocalRepositoryProvider.class, "maven").add(SingletonLiteral.INSTANCE);
323      event.addAnnotatedType(DefaultMetadataResolver.class, "maven").add(SingletonLiteral.INSTANCE);
324      event.addAnnotatedType(DefaultOfflineController.class, "maven").add(SingletonLiteral.INSTANCE);
325      event.addAnnotatedType(DefaultRemoteRepositoryManager.class, "maven").add(SingletonLiteral.INSTANCE);
326      event.addAnnotatedType(DefaultRepositoryConnectorProvider.class, "maven").add(SingletonLiteral.INSTANCE);
327      event.addAnnotatedType(DefaultRepositoryEventDispatcher.class, "maven").add(SingletonLiteral.INSTANCE);
328      event.addAnnotatedType(DefaultRepositoryLayoutProvider.class, "maven").add(SingletonLiteral.INSTANCE);
329      event.addAnnotatedType(DefaultRepositorySystem.class, "maven").add(SingletonLiteral.INSTANCE);
330      event.addAnnotatedType(DefaultSyncContextFactory.class, "maven").add(SingletonLiteral.INSTANCE);
331      event.addAnnotatedType(DefaultTransporterProvider.class, "maven").add(SingletonLiteral.INSTANCE);
332      event.addAnnotatedType(DefaultUpdateCheckManager.class, "maven").add(SingletonLiteral.INSTANCE);
333      event.addAnnotatedType(DefaultUpdatePolicyAnalyzer.class, "maven").add(SingletonLiteral.INSTANCE);
334      event.addAnnotatedType(EnhancedLocalRepositoryManagerFactory.class, "maven").add(SingletonLiteral.INSTANCE);
335      event.addAnnotatedType(Maven2RepositoryLayoutFactory.class, "maven").add(SingletonLiteral.INSTANCE);
336      event.addAnnotatedType(SimpleLocalRepositoryManagerFactory.class, "maven").add(SingletonLiteral.INSTANCE);
337      event.addAnnotatedType(Slf4jLoggerFactory.class, "maven");
338
339      //
340      // Types effectively bound by MavenRepositorySystemUtils
341      //
342
343      event.addAnnotatedType(ClassicDependencyManager.class, "maven").add(SingletonLiteral.INSTANCE);
344      event.addAnnotatedType(DefaultArtifactDescriptorReader.class, "maven").add(SingletonLiteral.INSTANCE);
345      event.addAnnotatedType(DefaultVersionRangeResolver.class, "maven").add(SingletonLiteral.INSTANCE);
346      event.addAnnotatedType(DefaultVersionResolver.class, "maven").add(SingletonLiteral.INSTANCE);
347      event.addAnnotatedType(FatArtifactTraverser.class, "maven").add(SingletonLiteral.INSTANCE);
348      event.addAnnotatedType(SnapshotMetadataGeneratorFactory.class, "maven");
349      event.addAnnotatedType(VersionsMetadataGeneratorFactory.class, "maven");
350
351      //
352      // Types effectively bound by DefaultModelBuilderFactory.  These
353      // all have @Singleton on them already.
354      //
355
356      event.addAnnotatedType(DefaultDependencyManagementImporter.class, "maven");
357      event.addAnnotatedType(DefaultDependencyManagementInjector.class, "maven");
358      event.addAnnotatedType(DefaultInheritanceAssembler.class, "maven");
359      event.addAnnotatedType(DefaultModelBuilder.class, "maven");
360      event.addAnnotatedType(DefaultModelLocator.class, "maven");
361      event.addAnnotatedType(DefaultModelNormalizer.class, "maven");
362      event.addAnnotatedType(DefaultModelPathTranslator.class, "maven");
363      event.addAnnotatedType(DefaultModelProcessor.class, "maven").add(Typed.Literal.of(new Class<?>[] { ModelProcessor.class }));
364      event.addAnnotatedType(DefaultModelReader.class, "maven");
365      event.addAnnotatedType(DefaultModelUrlNormalizer.class, "maven");
366      event.addAnnotatedType(DefaultModelValidator.class, "maven");
367      event.addAnnotatedType(DefaultPathTranslator.class, "maven");
368      event.addAnnotatedType(DefaultPluginConfigurationExpander.class, "maven");
369      event.addAnnotatedType(DefaultPluginManagementInjector.class, "maven");
370      event.addAnnotatedType(DefaultProfileInjector.class, "maven");
371      event.addAnnotatedType(DefaultReportConfigurationExpander.class, "maven");
372      event.addAnnotatedType(DefaultReportingConverter.class, "maven");
373      event.addAnnotatedType(DefaultSuperPomProvider.class, "maven");
374      event.addAnnotatedType(DefaultUrlNormalizer.class, "maven");
375      event.addAnnotatedType(FileProfileActivator.class, "maven");
376      event.addAnnotatedType(JdkVersionProfileActivator.class, "maven");
377      event.addAnnotatedType(OperatingSystemProfileActivator.class, "maven");
378      event.addAnnotatedType(PropertyProfileActivator.class, "maven");
379      event.addAnnotatedType(StringVisitorModelInterpolator.class, "maven");
380      event.addAnnotatedType(StubLifecycleBindingsInjector.class, "maven");
381
382      //
383      // Types bound by me :-)
384      //
385
386      // Somehow, the Producers nested class below is automatically
387      // discovered.  I think this is a bug.  If it is fixed, then
388      // uncomment this line.
389      // event.addAnnotatedType(Producers.class, "maven");
390      event.addAnnotatedType(BasicRepositoryConnectorFactory.class, "maven").add(SingletonLiteral.INSTANCE);
391      event.addAnnotatedType(DefaultSettingsBuilder.class, "maven");
392      event.addAnnotatedType(DefaultSettingsReader.class, "maven");
393      event.addAnnotatedType(DefaultSettingsValidator.class, "maven");
394      event.addAnnotatedType(DefaultSettingsWriter.class, "maven");
395      event.addAnnotatedType(FileTransporterFactory.class, "maven").add(SingletonLiteral.INSTANCE);
396      event.addAnnotatedType(HttpTransporterFactory.class, "maven").add(SingletonLiteral.INSTANCE);
397      event.addAnnotatedType(MavenSettingsMerger.class, "maven").add(SingletonLiteral.INSTANCE);
398    }
399  }
400
401  /**
402   * A hack to work around the fact that Maven's {@link
403   * FileProfileActivator} class and {@code maven-resolver-api}'s
404   * {@link FileTransporterFactory} both end up with the same CDI <a
405   * href="https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#name_resolution">EL
406   * name</a>.
407   *
408   * <p>This method arbitrarily removes the {@code file} EL name from
409   * the {@link FileProfileActivator} managed bean.</p>
410   *
411   * @param event the event in question; may be {@code null} in which
412   * case no action will be taken
413   *
414   * @see FileProfileActivator
415   *
416   * @see FileTransporterFactory
417   */
418  private final void removeBeanName(@Observes final ProcessBeanAttributes<FileProfileActivator> event) {
419    if (event != null) {
420      final BeanAttributes<FileProfileActivator> beanAttributes = event.getBeanAttributes();
421      if (beanAttributes != null) {
422        if ("file".equals(beanAttributes.getName())) {
423          event.configureBeanAttributes().name(null);
424        }
425      }
426    }
427  }
428
429  /**
430   * Returns a {@link Properties} object containing the <a
431   * href="https://maven.apache.org/shared/maven-archiver/#pom-properties-content"
432   * target="_parent">contents of a {@code pom.properties} file as
433   * commonly installed in {@code .jar} files by Maven</a>.
434   *
435   * <p>This method may return {@code null}.</p>
436   *
437   * <p>This method is not {@code final} due to CDI specification
438   * restrictions only.</p>
439   *
440   * @param location the location of the classpath element (a {@code
441   * .jar} file, or, perhaps, a directory); may be {@code null} in
442   * which case {@code null} will be returned
443   *
444   * @return a {@link Properties} object, or {@code null}
445   *
446   * @exception IOException if an input/output error occurs
447   *
448   * @exception URISyntaxException if there was a problem constructing
449   * a {@link URI}
450   */
451  public Properties getGroupArtifactVersionCoordinates(final URL location) throws IOException, URISyntaxException {
452    Properties returnValue = null;
453    if (location != null) {
454      final String path = location.getPath();
455
456      URL beanArchiveRoot = null;
457
458      String scheme = location.getProtocol();
459      if ("jar".equalsIgnoreCase(scheme)) {
460        beanArchiveRoot = new URL(location, "/"); // no jar entry, no matter what was passed in
461      } else if (path != null && path.endsWith(".jar")) {
462        beanArchiveRoot = new URL(new URL("jar", "", -1, location.toString() + "!/"), "/"); // normalize to jar URL with no jar entry
463      } else {
464        beanArchiveRoot = location; // no idea
465      }
466      assert beanArchiveRoot != null;
467
468      synchronized (this.coordinates) {
469        returnValue = this.coordinates.get(beanArchiveRoot.toURI());
470        if (returnValue == null) {
471
472          scheme = beanArchiveRoot.getProtocol();
473          if ("jar".equalsIgnoreCase(scheme)) {
474            final JarURLConnection urlConnection = (JarURLConnection)beanArchiveRoot.openConnection();
475            if (urlConnection != null) {
476              try (final JarFile jarFile = urlConnection.getJarFile()) {
477                if (jarFile != null) {
478                  returnValue = jarFile.stream()
479                    .filter(e -> {
480                        final String name = e.getName();
481                        return name != null && name.startsWith("META-INF/maven/") && name.endsWith("/pom.properties");
482                      })
483                    .findAny() // there should be only one
484                    .map(jarEntry -> {
485                        Properties p = null;
486                        try (final InputStream inputStream = new BufferedInputStream(jarFile.getInputStream(jarEntry))) {
487                          if (inputStream != null) {
488                            p = new Properties();
489                            p.load(inputStream);
490                          }
491                        } catch (final IOException ioException) {
492                          throw new RuntimeException(ioException);
493                        }
494                        return p;
495                      })
496                    .orElse(null);
497                }
498              }
499            }
500          } else if ("file".equalsIgnoreCase(scheme)) {
501            File root = new File(beanArchiveRoot.toURI());
502            while (root != null) {
503              if (root.isDirectory()) {
504                final File metaInfMaven = new File(root, "META-INF/maven");
505                if (metaInfMaven.isDirectory()) {
506
507                  File scanMe = metaInfMaven;
508                  while (scanMe != null && scanMe.isDirectory()) {
509
510                    final File[] subDirectories = scanMe.listFiles(f -> {
511                        if (f == null || !f.isDirectory()) {
512                          return false;
513                        }
514                        final String name = f.getName();
515                        return name != null && !name.startsWith(".");
516                      });
517
518                    if (subDirectories == null || subDirectories.length <= 0) {
519                      // We are looking for pom.properties in a
520                      // directory that has no further subdirectories.
521                      // We've found it.
522                      final File pomPropertiesFile = new File(scanMe, "pom.properties");
523                      scanMe = null;
524                      if (pomPropertiesFile.exists() && !pomPropertiesFile.isDirectory()) {
525                        returnValue = new Properties();
526                        try (final InputStream stream = new BufferedInputStream(Files.newInputStream(pomPropertiesFile.toPath()))) {
527                          returnValue.load(stream);
528                          beanArchiveRoot = root.toURI().toURL();
529                          root = null;
530                        }
531                      }
532                    } else if (subDirectories.length == 1) {
533                      scanMe = subDirectories[0];
534                    } else {
535                      scanMe = null;
536                    }
537                  }
538
539                } else {
540                  // For common local development purposes, see if
541                  // maven-archiver/pom.properties exists under our
542                  // root; this will give us coordinates for testing
543                  final File mavenArchiverPomProperties = new File(root, "maven-archiver/pom.properties");
544                  if (mavenArchiverPomProperties.exists() && !mavenArchiverPomProperties.isDirectory()) {
545                    returnValue = new Properties();
546                    try (final InputStream stream = new BufferedInputStream(Files.newInputStream(mavenArchiverPomProperties.toPath()))) {
547                      returnValue.load(stream);
548                      beanArchiveRoot = root.toURI().toURL();
549                      root = null;
550                    }
551                  }
552                }
553                if (root != null) {
554                  root = root.getParentFile();
555                }
556              } else {
557                root = root.getParentFile();
558              }
559            }
560          } else {
561            throw new IllegalStateException();
562          }
563
564          if (returnValue == null ||
565              !returnValue.containsKey("groupId") ||
566              !returnValue.containsKey("artifactId") ||
567              !returnValue.containsKey("version")) {
568            returnValue = null;
569          } else {
570            this.coordinates.put(beanArchiveRoot.toURI(), returnValue);
571          }
572        }
573      }
574    }
575    return returnValue;
576  }
577
578  /**
579   * Given a {@link Bean}, returns a {@link URL} representing the
580   * classpath root from which its {@linkplain Bean#getBeanClass()
581   * bean class} originates.
582   *
583   * <p>This method may return {@code null}.</p>
584   *
585   * @param bean the {@link Bean} whose classpath root should be
586   * returned; may be {@code null} in which case {@code null} will be
587   * returned
588   *
589   * @return a {@link URL} representing a classpath root from which
590   * the supplied {@link Bean}'s {@linkplain Bean#getBeanClass() bean
591   * class} originates, or {@code null}
592   *
593   * @see #getGroupArtifactVersionCoordinates(URL)
594   *
595   * @see Bean#getBeanClass()
596   *
597   * @see Class#getProtectionDomain()
598   *
599   * @see ProtectionDomain#getCodeSource()
600   *
601   * @see CodeSource#getLocation()
602   */
603  public static final URL getBeanArchiveLocation(final Bean<?> bean) {
604    URL returnValue = null;
605    if (bean != null) {
606      final Class<?> beanClass = bean.getBeanClass();
607      if (beanClass != null) {
608        final ProtectionDomain protectionDomain = beanClass.getProtectionDomain();
609        if (protectionDomain != null) {
610          final CodeSource codeSource = protectionDomain.getCodeSource();
611          if (codeSource != null) {
612            returnValue = codeSource.getLocation();
613          }
614        }
615      }
616    }
617    return returnValue;
618  }
619
620
621  /*
622   * Inner and nested classes.
623   */
624
625
626  /**
627   * A class housing several <a
628   * href="http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#producer_method">producer
629   * methods</a> that produce certain <a
630   * href="https://maven.apache.org/resolver/">Maven Artifact Resolver
631   * components</a>.
632   *
633   * @author <a href="http://about.me/lairdnelson"
634   * target="_parent">Laird Nelson</a>
635   */
636  private static final class Producers {
637
638
639    /*
640     * Static methods.
641     */
642
643
644    /**
645     * Produces a {@link MirrorSelector} in {@link Singleton} scope
646     * from the contents of a {@link Settings} object.
647     *
648     * <p>This method never returns {@code null}.</p>
649     *
650     * @param settingsInstance an {@link Instance} of {@link Settings}
651     * that may be {@linkplain Instance#isUnsatisfied() unsatisfied}
652     * that contains information about mirrors; must not be {@code
653     * null}
654     *
655     * @return a {@link MirrorSelector}; never {@code null}
656     *
657     * @exception NullPointerException if {@code settingsInstance} is
658     * {@code null}
659     */
660    @Produces
661    @Singleton
662    private static final MirrorSelector produceMirrorSelector(final Instance<Settings> settingsInstance) {
663      Objects.requireNonNull(settingsInstance);
664      final DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
665      if (settingsInstance.isResolvable()) {
666        final Collection<? extends Mirror> mirrors = settingsInstance.get().getMirrors();
667        if (mirrors != null && !mirrors.isEmpty()) {
668          for (final Mirror mirror : mirrors) {
669            assert mirror != null;
670            mirrorSelector.add(mirror.getId(),
671                               mirror.getUrl(),
672                               mirror.getLayout(),
673                               false, /* not a repository manager; settings.xml does not encode this information */
674                               mirror.getMirrorOf(),
675                               mirror.getMirrorOfLayouts());
676          }
677        }
678      }
679      return mirrorSelector;
680    }
681
682    /**
683     * Produces a {@link Settings} object in {@link Singleton} scope
684     * from the supplied {@link SettingsBuilder} object.
685     *
686     * <p>This method never returns {@code null}.</p>
687     *
688     * @param settingsBuilder the {@link SettingsBuilder} that will
689     * ultimately be used to create this method's return value; must
690     * not be {@code null}
691     *
692     * @param mavenHomePath a {@link Path} describing a Maven home
693     * directory for the purposes of locating the global settings
694     * file; may be {@code null}
695     *
696     * @param mavenConfPath a {@link Path} describing a Maven global
697     * configuration directory for the purposes of locating the global
698     * settings file; may be {@code null}
699     *
700     * @param globalSettingsPath a {@link Path} representing where the
701     * global {@code settings.xml} file lives; may be {@code null}
702     *
703     * @param userSettingsPath a {@link Path} representing where the
704     * user's {@code settings.xml} file lives; may be {@code null}
705     *
706     * @return a {@link Settings} object; never {@code null}
707     *
708     * @exception NullPointerException if {@code settingsBuilder} is
709     * {@code null}
710     *
711     * @exception SettingsBuildingException if the {@link
712     * SettingsBuildingResult#getProblems()} method invoked on the
713     * return value of the {@link
714     * SettingsBuilder#build(SettingsBuildingRequest)} method returns
715     * a {@link List} of {@link SettingsProblem}s
716     */
717    @Produces
718    @Singleton
719    private static final Settings produceSettings(final SettingsBuilder settingsBuilder,
720
721                                                  @ConfigurationValue(value = {
722                                                      "maven.home",
723                                                      "M2_HOME"
724                                                    })
725                                                  Path mavenHomePath,
726
727                                                  @ConfigurationValue(value = {
728                                                      "maven.conf"
729                                                    })
730                                                  Path mavenConfPath,
731
732                                                  @ConfigurationValue(value = {
733                                                      "org.microbean.maven.cdi.globalSettingsPath",
734                                                      "GLOBAL_SETTINGS_PATH"
735                                                    })
736                                                  Path globalSettingsPath,
737
738                                                  @ConfigurationValue(value = {
739                                                      "org.microbean.maven.cdi.userSettingsPath",
740                                                      "USER_SETTINGS_PATH"
741                                                    },
742                                                    defaultValue = "${configurations[\"user.home\"]}/.m2/settings.xml")
743                                                  final Path userSettingsPath)
744      throws SettingsBuildingException {
745      Objects.requireNonNull(settingsBuilder);
746      final DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
747      request.setSystemProperties(System.getProperties());
748      // request.setUserProperties(userProperties); // TODO: implement this
749
750      final File globalSettingsFile;
751      if (globalSettingsPath == null) {
752        if (mavenConfPath == null) {
753          if (mavenHomePath == null) {
754            String m2Home = System.getProperty("maven.home");
755            if (m2Home == null) {
756              m2Home = System.getenv("M2_HOME");
757            }
758            if (m2Home == null) {
759              globalSettingsFile = null;
760            } else {
761              mavenHomePath = Paths.get(m2Home);
762              mavenConfPath = mavenHomePath.resolve("conf");
763              globalSettingsPath = mavenConfPath.resolve("settings.xml");
764              globalSettingsFile = globalSettingsPath.toFile();
765            }
766          } else {
767            mavenConfPath = mavenHomePath.resolve("conf");
768            assert mavenConfPath != null;
769            globalSettingsPath = mavenConfPath.resolve("settings.xml");
770            globalSettingsFile = globalSettingsPath.toFile();
771          }
772        } else {
773          globalSettingsPath = mavenConfPath.resolve("settings.xml");
774          globalSettingsFile = globalSettingsPath.toFile();
775        }
776      } else {
777        globalSettingsFile = globalSettingsPath.toFile();
778      }
779      request.setGlobalSettingsFile(globalSettingsFile);
780
781      final File userSettingsFile;
782      if (userSettingsPath == null) {
783        userSettingsFile = Paths.get(System.getProperty("user.home"), ".m2", "settings.xml").toFile();
784      } else {
785        userSettingsFile = userSettingsPath.toFile();
786      }
787      request.setUserSettingsFile(userSettingsFile);
788      final SettingsBuildingResult settingsBuildingResult = settingsBuilder.build(request);
789      assert settingsBuildingResult != null;
790      final List<SettingsProblem> settingsBuildingProblems = settingsBuildingResult.getProblems();
791      if (settingsBuildingProblems != null && !settingsBuildingProblems.isEmpty()) {
792        // It is guaranteed that these problems will contain warnings
793        // only, but that's still a problem!
794        throw new SettingsBuildingException(settingsBuildingProblems);
795      }
796      final Settings returnValue = settingsBuildingResult.getEffectiveSettings();
797      return returnValue;
798    }
799
800    /**
801     * Produces a {@link ProfileSelector} in {@link Singleton} scope
802     * that makes use of the supplied {@link ProfileActivator}s.
803     *
804     * <p>This method never returns {@code null}.</p>
805     *
806     * @param jdkVersionProfileActivator a {@link ProfileActivator}
807     * that can activate a Maven profile based on the version of the
808     * JDK; may be {@code null}
809     *
810     * @param operatingSystemProfileActivator a {@link
811     * ProfileActivator} that can activate a Maven profile based on
812     * the current operating system; may be {@code null}
813     *
814     * @param fileProfileActivator a {@link ProfileActivator} that can
815     * activate a Maven profile based on the presence of a file; may
816     * be {@code null}
817     *
818     * @param propertyProfileActivator a {@link ProfileActivator} that
819     * can activate a maven profile based on the value of a property;
820     * may be {@code null}
821     *
822     * @return a {@link ProfileSelector}; never {@code null}
823     *
824     * @see JdkVersionProfileActivator
825     *
826     * @see OperatingSystemProfileActivator
827     *
828     * @see FileProfileActivator
829     *
830     * @see PropertyProfileActivator
831     *
832     * @see DefaultProfileSelector
833     */
834    @Produces
835    @Singleton
836    private static final ProfileSelector produceProfileSelector(@Named("jdk-version") final ProfileActivator jdkVersionProfileActivator,
837                                                                @Named("os") final ProfileActivator operatingSystemProfileActivator,
838                                                                @Named("property") final ProfileActivator fileProfileActivator,
839                                                                @Named("file") final ProfileActivator propertyProfileActivator) {
840      final DefaultProfileSelector returnValue = new DefaultProfileSelector();
841      returnValue.addProfileActivator(jdkVersionProfileActivator);
842      returnValue.addProfileActivator(operatingSystemProfileActivator);
843      returnValue.addProfileActivator(propertyProfileActivator);
844      returnValue.addProfileActivator(fileProfileActivator); // TODO: make sure fileProfileActivator.pathTranslator is non-null (i.e. injection worked)
845      return returnValue;
846    }
847
848    /**
849     * Produces an {@link ILoggerFactory} in {@link Singleton} scope.
850     *
851     * <p>This method never returns {@code null}.</p>
852     *
853     * @return an {@link ILoggerFactory}; never {@code null}
854     *
855     * @see LoggerFactory#getILoggerFactory()
856     */
857    @Produces
858    @Singleton
859    private static final ILoggerFactory produceILoggerFactory() {
860      return LoggerFactory.getILoggerFactory();
861    }
862
863    /**
864     * Produces a {@link LocalRepository} in {@link Singleton} scope.
865     *
866     * <p>This method never returns {@code null}.</p>
867     *
868     * @param mavenRepoLocal the value of the {@code maven.repo.local}
869     * configuration property; may be (and often is) {@code null}
870     *
871     * @param settingsInstance an {@link Instance} of {@link Settings}
872     * that may be {@linkplain Instance#isUnsatisfied() unsatisfied}
873     * that contains information about where a local Maven repository
874     * can be found; must not be {@code null}
875     *
876     * @return a {@link LocalRepository}; never {@code null}
877     *
878     * @exception NullPointerException if {@code settingsInstance} is
879     * {@code null}
880     *
881     * @see Settings#getLocalRepository()
882     */
883    @Produces
884    @Singleton
885    private static final LocalRepository produceLocalRepository(@ConfigurationValue("maven.repo.local")
886                                                                final String mavenRepoLocal,
887                                                                final Instance<Settings> settingsInstance) {
888      // TODO: see https://github.com/apache/maven/blob/eb6b212b567c287734a2dbbef3c113fe650f1def/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java#L133
889      Objects.requireNonNull(settingsInstance);
890      String localRepositoryString = mavenRepoLocal;
891      if (localRepositoryString == null && settingsInstance.isResolvable()) {
892        localRepositoryString = settingsInstance.get().getLocalRepository();
893      }
894      if (localRepositoryString == null) {
895        localRepositoryString = System.getProperty("user.home") + "/.m2/repository";
896      }
897      final LocalRepository returnValue = new LocalRepository(localRepositoryString);
898      return returnValue;
899    }
900
901    /**
902     * Produces a {@link List} of {@link RemoteRepository} instances
903     * in {@link Dependent} scope {@linkplain Resolution suitable for
904     * artifact resolution} whose contents are inferred from reading
905     * the {@linkplain Settings#getActiveProfiles() active profiles
906     * contained in the supplied <code>Settings</code> object}.
907     *
908     * <p>This method will never return {@code null}.</p>
909     *
910     * @param settingsInstance an {@link Instance} of {@link Settings}
911     * that may be {@linkplain Instance#isUnsatisfied() unsatisfied}
912     * that contains information about where remote Maven repositories
913     * may be found; must not be {@code null}
914     *
915     * @param repositorySystem the {@link RepositorySystem} whose
916     * {@link
917     * RepositorySystem#newResolutionRepositories(RepositorySystemSession,
918     * List)} method will be called; must not be {@code null}
919     *
920     * @param session the {@link RepositorySystemSession} currently in
921     * effect; must not be {@code null}
922     *
923     * @return a {@link List} of {@link RemoteRepository} instances
924     * {@link Resolution suitable for artifact resolution}; never
925     * {@code null}
926     *
927     * @exception NullPointerException if either {@code
928     * settingsInstance}, {@code repositorySystem} or {@code session}
929     * is {@code null}
930     */
931    @Produces
932    @Dependent
933    @Resolution
934    private static final List<RemoteRepository> produceRemoteRepositoryList(final Instance<Settings> settingsInstance,
935                                                                            final RepositorySystem repositorySystem,
936                                                                            final RepositorySystemSession session) {
937      Objects.requireNonNull(settingsInstance);
938      Objects.requireNonNull(repositorySystem);
939      Objects.requireNonNull(session);
940      List<RemoteRepository> remoteRepositories = new ArrayList<>();
941      if (settingsInstance.isResolvable()) {
942        final Settings settings = settingsInstance.get();
943        assert settings != null;
944        final Map<String, Profile> profiles = settings.getProfilesAsMap();
945        if (profiles != null && !profiles.isEmpty()) {
946          final Collection<String> activeProfileKeys = settings.getActiveProfiles();
947          if (activeProfileKeys != null && !activeProfileKeys.isEmpty()) {
948            for (final String activeProfileKey : activeProfileKeys) {
949              final Profile activeProfile = profiles.get(activeProfileKey);
950              if (activeProfile != null) {
951                final Collection<Repository> repositories = activeProfile.getRepositories();
952                if (repositories != null && !repositories.isEmpty()) {
953                  for (final Repository repository : repositories) {
954                    if (repository != null) {
955                      RemoteRepository.Builder builder = new RemoteRepository.Builder(repository.getId(), repository.getLayout(), repository.getUrl());
956
957                      final org.apache.maven.settings.RepositoryPolicy settingsReleasePolicy = repository.getReleases();
958                      if (settingsReleasePolicy != null) {
959                        final org.eclipse.aether.repository.RepositoryPolicy releasePolicy = new org.eclipse.aether.repository.RepositoryPolicy(settingsReleasePolicy.isEnabled(), settingsReleasePolicy.getUpdatePolicy(), settingsReleasePolicy.getChecksumPolicy());
960                        builder = builder.setReleasePolicy(releasePolicy);
961                      }
962
963                      final org.apache.maven.settings.RepositoryPolicy settingsSnapshotPolicy = repository.getSnapshots();
964                      if (settingsSnapshotPolicy != null) {
965                        final org.eclipse.aether.repository.RepositoryPolicy snapshotPolicy = new org.eclipse.aether.repository.RepositoryPolicy(settingsSnapshotPolicy.isEnabled(), settingsSnapshotPolicy.getUpdatePolicy(), settingsSnapshotPolicy.getChecksumPolicy());
966                        builder = builder.setSnapshotPolicy(snapshotPolicy);
967                      }
968
969                      final RemoteRepository remoteRepository = builder.build();
970                      assert remoteRepository != null;
971                      remoteRepositories.add(remoteRepository);
972                    }
973                  }
974                }
975              }
976            }
977          }
978        }
979      }
980      final RemoteRepository mavenCentral = new RemoteRepository.Builder("central", "default", "http://central.maven.org/maven2/").build();
981      assert mavenCentral != null;
982      remoteRepositories.add(mavenCentral);
983      remoteRepositories = repositorySystem.newResolutionRepositories(session, remoteRepositories);
984      assert remoteRepositories != null;
985      return remoteRepositories;
986    }
987
988    /**
989     * Produces a {@link RepositorySystemSession} in {@link Dependent} scope.
990     *
991     * <p>This method never returns {@code null}.</p>
992     *
993     * <h2>Implementation Notes</h2>
994     *
995     * <p>This method returns an {@link RepositorySystemSession} that
996     * is built in the same manner as that built by the {@link
997     * MavenRepositorySystemUtils#newSession()} method.</p>
998     *
999     * @param dependencyTraverser the {@link DependencyTraverser} that
1000     * helps in pruning the dependency graph; may be {@code null}
1001     *
1002     * @param dependencyManager the {@link DependencyManager} that
1003     * applies any overriding concerns with respect to particular
1004     * dependencies' versions and scopes (think {@code
1005     * <dependencyManagement>} in a Maven {@code pom.xml} file); may
1006     * be {@code null}
1007     *
1008     * @param dependencySelector a {@link DependencySelector} that
1009     * helps filter dependencies; may be {@code null}
1010     *
1011     * @param dependencyGraphTransformer a {@link
1012     * DependencyGraphTransformer} that helps with conflict
1013     * resolution; may be {@code null}
1014     *
1015     * @param artifactTypeRegistry an {@link ArtifactTypeRegistry}
1016     * that governs what artifacts will be looked at; may be {@code
1017     * null}
1018     *
1019     * @param artifactDescriptorPolicy an {@link
1020     * ArtifactDescriptorPolicy} that controls what to do with missing
1021     * or invalid artifacts; may be {@code null}
1022     *
1023     * @param settingsInstance an {@link Instance} of {@link Settings}
1024     * that may be {@linkplain Instance#isUnsatisfied() unsatisfied}
1025     * that may be consulted for offline status information; must not
1026     * be {@code null}
1027     *
1028     * @param mirrorSelector a {@link MirrorSelector} that will choose
1029     * the appropriate mirror for a given repository; may be {@code
1030     * null}
1031     *
1032     * @param localRepository a {@link LocalRepository} instance
1033     * representing the location where remote artifacts will be
1034     * cached; must not be {@code null}
1035     *
1036     * @param localRepositoryProvider a {@link
1037     * LocalRepositoryProvider} that will be {@linkplain
1038     * LocalRepositoryProvider#newLocalRepositoryManager(RepositorySystemSession,
1039     * LocalRepository) used to obtain a
1040     * <code>LocalRepositoryManager</code>}; must not be {@code null}
1041     *
1042     * @param transferListenerInstance an {@link Instance}
1043     * representing a {@link TransferListener}, which, {@linkplain
1044     * Instance#isResolvable() if resolvable}, will be installed on
1045     * the returned {@link RepositorySystemSession}; may be {@code
1046     * null}
1047     *
1048     * @return a {@link RepositorySystemSession}; never {@code null}
1049     *
1050     * @exception NullPointerException if any parameter described
1051     * above that must be non-{@code null} was {@code null}
1052     *
1053     * @exception NoLocalRepositoryManagerException if the supplied
1054     * {@link LocalRepositoryProvider} could not {@linkplain
1055     * LocalRepositoryProvider#newLocalRepositoryManager(RepositorySystemSession,
1056     * LocalRepository) provide a <code>LocalRepositoryManager</code>}
1057     *
1058     * @see MavenRepositorySystemUtils#newSession()
1059     *
1060     * @see FatArtifactTraverser
1061     *
1062     * @see ClassicDependencyManager
1063     *
1064     * @see ScopeDependencySelector
1065     *
1066     * @see OptionalDependencySelector
1067     *
1068     * @see ExclusionDependencySelector
1069     *
1070     * @see ConflictResolver
1071     *
1072     * @see NearestVersionSelector
1073     *
1074     * @see JavaScopeSelector
1075     *
1076     * @see SimpleOptionalitySelector
1077     *
1078     * @see JavaScopeDeriver
1079     *
1080     * @see #produceArtifactTypeRegistry()
1081     *
1082     * @see #produceArtifactDescriptorPolicy()
1083     *
1084     * @see Settings#isOffline()
1085     *
1086     * @see DefaultRepositorySystemSession#setOffline(boolean)
1087     *
1088     * @see #produceMirrorSelector(Instance)
1089     *
1090     * @see #produceLocalRepository(String, Instance)
1091     *
1092     * @see
1093     * LocalRepositoryProvider#newLocalRepositoryManager(RepositorySystemSession,
1094     * LocalRepository)
1095     *
1096     * @see TransferListener
1097     *
1098     * @see
1099     * DefaultRepositorySystemSession#setTransferListener(TransferListener)
1100     */
1101    @Produces
1102    @Dependent
1103    private static final RepositorySystemSession produceRepositorySystemSession(final DependencyTraverser dependencyTraverser,
1104                                                                                final DependencyManager dependencyManager,
1105                                                                                final DependencySelector dependencySelector,
1106                                                                                final DependencyGraphTransformer dependencyGraphTransformer,
1107                                                                                final ArtifactTypeRegistry artifactTypeRegistry,
1108                                                                                final ArtifactDescriptorPolicy artifactDescriptorPolicy,
1109                                                                                final Instance<Settings> settingsInstance,
1110                                                                                final MirrorSelector mirrorSelector,
1111                                                                                final LocalRepository localRepository,
1112                                                                                final LocalRepositoryProvider localRepositoryProvider,
1113                                                                                final Instance<TransferListener> transferListenerInstance)
1114    throws NoLocalRepositoryManagerException {
1115      Objects.requireNonNull(settingsInstance);
1116      final DefaultRepositorySystemSession session = new DefaultRepositorySystemSession();
1117      session.setDependencyTraverser(dependencyTraverser);
1118      session.setDependencyManager(dependencyManager);
1119      session.setDependencySelector(dependencySelector);
1120      session.setDependencyGraphTransformer(dependencyGraphTransformer);
1121      session.setArtifactTypeRegistry(artifactTypeRegistry);
1122      session.setArtifactDescriptorPolicy(artifactDescriptorPolicy);
1123      final Properties sessionSystemProperties = new Properties();
1124      final Properties systemProperties = System.getProperties();
1125      synchronized (systemProperties) {
1126        final Set<String> keys = systemProperties.stringPropertyNames();
1127        if (keys != null) {
1128          for (final String key : keys) {
1129            assert key != null;
1130            sessionSystemProperties.setProperty(key, systemProperties.getProperty(key));
1131          }
1132        }
1133      }
1134      session.setSystemProperties(sessionSystemProperties);
1135      session.setConfigProperties(sessionSystemProperties);
1136      if (settingsInstance.isResolvable()) {
1137        session.setOffline(settingsInstance.get().isOffline());
1138      }
1139      session.setMirrorSelector(mirrorSelector);
1140      final LocalRepositoryManager localRepositoryManager = localRepositoryProvider.newLocalRepositoryManager(session, localRepository);
1141      session.setLocalRepositoryManager(localRepositoryManager);
1142      if (transferListenerInstance != null && transferListenerInstance.isResolvable()) {
1143        session.setTransferListener(transferListenerInstance.get());
1144      }
1145      return session;
1146    }
1147
1148    /**
1149     * Produces a {@link DependencySelector} in {@link Singleton}
1150     * scope.
1151     *
1152     * <p>This method never returns {@code null}.</p>
1153     *
1154     * <h2>Implementation Notes</h2>
1155     *
1156     * <p>This method returns an {@link AndDependencySelector} that is
1157     * built in the same manner as that built by the {@link
1158     * MavenRepositorySystemUtils#newSession()} method.</p>
1159     *
1160     * @return a {@link DependencySelector}; never {@code null}
1161     *
1162     * @see MavenRepositorySystemUtils#newSession()
1163     */
1164    @Produces
1165    @Singleton
1166    private static final DependencySelector produceDependencySelector() {
1167      return new AndDependencySelector(new ScopeDependencySelector("test", "provided"), // excludes test and provided scopes
1168                                       new OptionalDependencySelector(),
1169                                       new ExclusionDependencySelector());
1170    }
1171
1172    /**
1173     * Produces a {@link DependencyGraphTransformer} in {@link
1174     * Singleton} scope.
1175     *
1176     * <p>This method never returns {@code null}.</p>
1177     *
1178     * <h2>Implementation Notes</h2>
1179     *
1180     * <p>This method returns a {@link
1181     * ChainedDependencyGraphTransformer} that is built in the same
1182     * manner as that built by the {@link
1183     * MavenRepositorySystemUtils#newSession()} method.</p>
1184     *
1185     * @return a {@link DependencyGraphTransformer}; never {@code
1186     * null}
1187     *
1188     * @see MavenRepositorySystemUtils#newSession()
1189     */
1190    @Produces
1191    @Singleton
1192    private static final DependencyGraphTransformer produceDependencyGraphTransformer() {
1193      return new ChainedDependencyGraphTransformer(new ConflictResolver(new NearestVersionSelector(),
1194                                                                        new JavaScopeSelector(),
1195                                                                        new SimpleOptionalitySelector(),
1196                                                                        new JavaScopeDeriver()),
1197                                                   new JavaDependencyContextRefiner());
1198    }
1199
1200    /**
1201     * Produces an {@link ArtifactTypeRegistry} in {@link Singleton}
1202     * scope.
1203     *
1204     * <p>This method never returns {@code null}.</p>
1205     *
1206     * <h2>Implementation Notes</h2>
1207     *
1208     * <p>This method returns a {@link DefaultArtifactTypeRegistry}
1209     * that is built in the same manner as that built by the {@link
1210     * MavenRepositorySystemUtils#newSession()} method.</p>
1211     *
1212     * @return an {@link ArtifactTypeRegistry}; never {@code null}
1213     *
1214     * @see MavenRepositorySystemUtils#newSession()
1215     */
1216    @Produces
1217    @Singleton
1218    private static final ArtifactTypeRegistry produceArtifactTypeRegistry() {
1219      final DefaultArtifactTypeRegistry stereotypes = new DefaultArtifactTypeRegistry();
1220      stereotypes.add(new DefaultArtifactType("pom"));
1221      stereotypes.add(new DefaultArtifactType("maven-plugin", "jar", "", "java"));
1222      stereotypes.add(new DefaultArtifactType("jar", "jar", "", "java"));
1223      stereotypes.add(new DefaultArtifactType("ejb", "jar", "", "java"));
1224      stereotypes.add(new DefaultArtifactType("ejb-client", "jar", "client", "java"));
1225      stereotypes.add(new DefaultArtifactType("test-jar", "jar", "tests", "java"));
1226      stereotypes.add(new DefaultArtifactType("javadoc", "jar", "javadoc", "java"));
1227      stereotypes.add(new DefaultArtifactType("java-source", "jar", "sources", "java", false, false));
1228      stereotypes.add(new DefaultArtifactType("war", "war", "", "java", false, true));
1229      stereotypes.add(new DefaultArtifactType("ear", "ear", "", "java", false, true));
1230      stereotypes.add(new DefaultArtifactType("rar", "rar", "", "java", false, true));
1231      stereotypes.add(new DefaultArtifactType("par", "par", "", "java", false, true));
1232      return stereotypes;
1233    }
1234
1235    /**
1236     * Produces an {@link ArtifactDescriptorPolicy} in {@link
1237     * Singleton} scope.
1238     *
1239     * <p>This method never returns {@code null}.</p>
1240     *
1241     * <h2>Implementation Notes</h2>
1242     *
1243     * <p>This method returns a {@link SimpleArtifactDescriptorPolicy}
1244     * that is built in the same manner as that built by the {@link
1245     * MavenRepositorySystemUtils#newSession()} method.</p>
1246     *
1247     * @return an {@link ArtifactDescriptorPolicy}; never {@code null}
1248     *
1249     * @see MavenRepositorySystemUtils#newSession()
1250     */
1251    @Produces
1252    @Singleton
1253    private static final ArtifactDescriptorPolicy produceArtifactDescriptorPolicy() {
1254      return new SimpleArtifactDescriptorPolicy(true /* ignoreMissing */, true /* ignoreInvalid */);
1255    }
1256
1257
1258    /*
1259     * Sets of things.
1260     */
1261
1262
1263    /**
1264     * Converts an {@link Instance} of {@link
1265     * LocalRepositoryManagerFactory} instances into a {@link Set} of
1266     * such instances in {@link Singleton} scope.
1267     *
1268     * <p>This method never returns {@code null}.</p>
1269     *
1270     * @param localRepositoryManagerFactories an {@link Instance} of
1271     * {@link LocalRepositoryManagerFactory} instances; may be {@code
1272     * null}
1273     *
1274     * @return a {@link Set} of {@link LocalRepositoryManagerFactory}
1275     * instances; never {@code null}
1276     *
1277     * @see #produceSet(Instance)
1278     */
1279    @Produces
1280    @Singleton
1281    private static final Set<LocalRepositoryManagerFactory> produceLocalRepositoryManagerFactorySet(@Any final Instance<LocalRepositoryManagerFactory> localRepositoryManagerFactories) {
1282      final Set<LocalRepositoryManagerFactory> returnValue = produceSet(localRepositoryManagerFactories);
1283      return returnValue;
1284    }
1285
1286    /**
1287     * Converts an {@link Instance} of {@link RepositoryLayoutFactory}
1288     * instances into a {@link Set} of such instances in {@link
1289     * Singleton} scope.
1290     *
1291     * <p>This method never returns {@code null}.</p>
1292     *
1293     * @param repositoryLayoutFactories an {@link Instance} of {@link
1294     * RepositoryLayoutFactory} instances; may be {@code null}
1295     *
1296     * @return a {@link Set} of {@link RepositoryLayoutFactory}
1297     * instances; never {@code null}
1298     *
1299     * @see #produceSet(Instance)
1300     */
1301    @Produces
1302    @Singleton
1303    private static final Set<RepositoryLayoutFactory> produceRepositoryLayoutFactorySet(@Any final Instance<RepositoryLayoutFactory> repositoryLayoutFactories) {
1304      return produceSet(repositoryLayoutFactories);
1305    }
1306
1307    /**
1308     * Converts an {@link Instance} of {@link TransporterFactory}
1309     * instances into a {@link Set} of such instances in {@link
1310     * Singleton} scope.
1311     *
1312     * <p>This method never returns {@code null}.</p>
1313     *
1314     * @param transporterFactories an {@link Instance} of {@link
1315     * TransporterFactory} instances; may be {@code null}
1316     *
1317     * @return a {@link Set} of {@link TransporterFactory} instances;
1318     * never {@code null}
1319     *
1320     * @see #produceSet(Instance)
1321     */
1322    @Produces
1323    @Singleton
1324    private static final Set<TransporterFactory> produceTransporterFactorySet(@Any final Instance<TransporterFactory> transporterFactories) {
1325      return produceSet(transporterFactories);
1326    }
1327
1328    /**
1329     * Converts an {@link Instance} of {@link RepositoryListener}
1330     * instances into a {@link Set} of such instances in {@link
1331     * Singleton} scope.
1332     *
1333     * <p>This method never returns {@code null}.</p>
1334     *
1335     * @param repositoryListeners an {@link Instance} of {@link
1336     * RepositoryListener} instances; may be {@code null}
1337     *
1338     * @return a {@link Set} of {@link RepositoryListener} instances;
1339     * never {@code null}
1340     *
1341     * @see #produceSet(Instance)
1342     */
1343    @Produces
1344    @Dependent
1345    private static final Set<RepositoryListener> produceRepositoryListenerSet(@Any final Instance<RepositoryListener> repositoryListeners) {
1346      return produceSet(repositoryListeners);
1347    }
1348
1349    /**
1350     * Converts an {@link Instance} of {@link
1351     * RepositoryConnectorFactory} instances into a {@link Set} of
1352     * such instances in {@link Singleton} scope.
1353     *
1354     * <p>This method never returns {@code null}.</p>
1355     *
1356     * @param repositoryConnectorFactories an {@link Instance} of
1357     * {@link RepositoryConnectorFactory} instances; may be {@code
1358     * null}
1359     *
1360     * @return a {@link Set} of {@link RepositoryConnectorFactory}
1361     * instances; never {@code null}
1362     *
1363     * @see #produceSet(Instance)
1364     */
1365    @Produces
1366    @Singleton
1367    private static final Set<RepositoryConnectorFactory> produceRepositoryConnectorFactorySet(@Any final Instance<RepositoryConnectorFactory> repositoryConnectorFactories) {
1368      return produceSet(repositoryConnectorFactories);
1369    }
1370
1371    /**
1372     * Converts an {@link Instance} of {@link
1373     * MetadataGeneratorFactory} instances into a {@link Set} of such
1374     * instances in {@link Singleton} scope.
1375     *
1376     * <p>This method never returns {@code null}.</p>
1377     *
1378     * @param metadataGeneratorFactories an {@link Instance} of {@link
1379     * MetadataGeneratorFactory} instances; may be {@code null}
1380     *
1381     * @return a {@link Set} of {@link MetadataGeneratorFactory}
1382     * instances; never {@code null}
1383     *
1384     * @see #produceSet(Instance)
1385     */
1386    @Produces
1387    @Singleton
1388    private static final Set<MetadataGeneratorFactory> produceMetadataGeneratorFactorySet(@Any final Instance<MetadataGeneratorFactory> metadataGeneratorFactories) {
1389      return produceSet(metadataGeneratorFactories);
1390    }
1391
1392    /**
1393     * Returns a {@link Set} of the objects represented by the
1394     * supplied {@link Instance}.
1395     *
1396     * <p>This method never returns {@code null}.</p>
1397     *
1398     * @param <T> the type of object the returned {@link Set} will
1399     * contain
1400     *
1401     * @param things the {@link Instance} whose instances should be
1402     * returned as a {@link Set}; may be {@code null}
1403     *
1404     * @return a non-{@code null} {@link Set}
1405     */
1406    private static final <T> Set<T> produceSet(final Instance<? extends T> things) {
1407      final Set<T> returnValue = new HashSet<>();
1408      if (things != null) {
1409        for (final T thing : things) {
1410          returnValue.add(thing);
1411        }
1412      }
1413      return returnValue;
1414    }
1415
1416  }
1417
1418  /**
1419   * A {@link LifecycleBindingsInjector} that does nothing.
1420   *
1421   * @author <a href="http://about.me/lairdnelson/"
1422   * target="_parent">Laird Nelson</a>
1423   *
1424   * @see LifecycleBindingsInjector
1425   */
1426  private static final class StubLifecycleBindingsInjector implements LifecycleBindingsInjector {
1427
1428
1429    /*
1430     * Instance methods.
1431     */
1432
1433
1434    /**
1435     * Does nothing when invoked.
1436     *
1437     * @param model ignored
1438     *
1439     * @param modelBuildingRequest ignored
1440     *
1441     * @param modelProblemCollector ignored
1442     */
1443    @Override
1444    public final void injectLifecycleBindings(final Model model, final ModelBuildingRequest modelBuildingRequest, final ModelProblemCollector modelProblemCollector) {
1445
1446    }
1447
1448  }
1449
1450}