001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2020-2024 Andres Almiray. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.kordamp.maven.checker.cli.internal; 019 020import com.google.common.base.CharMatcher; 021import com.google.common.collect.ImmutableMap; 022import org.apache.maven.execution.DefaultMavenExecutionRequest; 023import org.apache.maven.execution.MavenExecutionRequest; 024import org.apache.maven.project.MavenProject; 025import org.apache.maven.project.ProjectBuilder; 026import org.apache.maven.project.ProjectBuildingException; 027import org.apache.maven.project.ProjectBuildingRequest; 028import org.apache.maven.project.ProjectBuildingResult; 029import org.apache.maven.repository.internal.MavenRepositorySystemUtils; 030import org.codehaus.plexus.ContainerConfiguration; 031import org.codehaus.plexus.DefaultContainerConfiguration; 032import org.codehaus.plexus.DefaultPlexusContainer; 033import org.codehaus.plexus.PlexusConstants; 034import org.codehaus.plexus.PlexusContainer; 035import org.codehaus.plexus.PlexusContainerException; 036import org.codehaus.plexus.classworlds.ClassWorld; 037import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 038import org.eclipse.aether.DefaultRepositorySystemSession; 039import org.eclipse.aether.RepositorySystem; 040import org.eclipse.aether.RepositorySystemSession; 041import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; 042import org.eclipse.aether.impl.DefaultServiceLocator; 043import org.eclipse.aether.repository.LocalRepository; 044import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; 045import org.eclipse.aether.spi.connector.transport.TransporterFactory; 046import org.eclipse.aether.transport.file.FileTransporterFactory; 047import org.eclipse.aether.transport.http.HttpTransporterFactory; 048 049import java.io.File; 050import java.io.IOException; 051import java.nio.file.Files; 052import java.nio.file.Path; 053import java.nio.file.Paths; 054import java.util.Locale; 055import java.util.Properties; 056 057/** 058 * @author Andres Almiray 059 * @since 1.1.0 060 */ 061public class PomParser { 062 private static final CharMatcher LOWER_ALPHA_NUMERIC = 063 CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9')); 064 065 public static MavenProject createMavenProject(File pomFile) { 066 return createMavenProject(pomFile, createDefaultRepositorySystemSession(newRepositorySystem())); 067 } 068 069 private static MavenProject createMavenProject(File pomFile, RepositorySystemSession session) { 070 // MavenCli's way to instantiate PlexusContainer 071 ClassWorld classWorld = 072 new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader()); 073 ContainerConfiguration containerConfiguration = 074 new DefaultContainerConfiguration() 075 .setClassWorld(classWorld) 076 .setRealm(classWorld.getClassRealm("plexus.core")) 077 .setClassPathScanning(PlexusConstants.SCANNING_INDEX) 078 .setAutoWiring(true) 079 .setJSR250Lifecycle(true) 080 .setName("pom-reader"); 081 try { 082 PlexusContainer container = new DefaultPlexusContainer(containerConfiguration); 083 084 MavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest(); 085 ProjectBuildingRequest projectBuildingRequest = 086 mavenExecutionRequest.getProjectBuildingRequest(); 087 088 projectBuildingRequest.setRepositorySession(session); 089 090 // Profile activation needs properties such as JDK version 091 Properties properties = new Properties(); // allowing duplicate entries 092 properties.putAll(projectBuildingRequest.getSystemProperties()); 093 properties.putAll(detectOsProperties()); 094 properties.putAll(System.getProperties()); 095 projectBuildingRequest.setSystemProperties(properties); 096 097 ProjectBuilder projectBuilder = container.lookup(ProjectBuilder.class); 098 ProjectBuildingResult projectBuildingResult = 099 projectBuilder.build(pomFile, projectBuildingRequest); 100 return projectBuildingResult.getProject(); 101 } catch (PlexusContainerException | ComponentLookupException | ProjectBuildingException ex) { 102 throw new IllegalStateException(ex); 103 } 104 } 105 106 private static RepositorySystem newRepositorySystem() { 107 DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); 108 locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); 109 locator.addService(TransporterFactory.class, FileTransporterFactory.class); 110 locator.addService(TransporterFactory.class, HttpTransporterFactory.class); 111 112 return locator.getService(RepositorySystem.class); 113 } 114 115 private static DefaultRepositorySystemSession createDefaultRepositorySystemSession(RepositorySystem system) { 116 DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); 117 LocalRepository localRepository = new LocalRepository(findLocalRepository()); 118 session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepository)); 119 return session; 120 } 121 122 private static String findLocalRepository() { 123 Path home = Paths.get(System.getProperty("user.home")); 124 Path localRepo = home.resolve(".m2").resolve("repository"); 125 if (Files.isDirectory(localRepo)) { 126 return localRepo.toAbsolutePath().toString(); 127 } else { 128 return makeTemporaryLocalRepository(); 129 } 130 } 131 132 private static String makeTemporaryLocalRepository() { 133 try { 134 File temporaryDirectory = Files.createTempDirectory("m2").toFile(); 135 temporaryDirectory.deleteOnExit(); 136 return temporaryDirectory.getAbsolutePath(); 137 } catch (IOException ex) { 138 return null; 139 } 140 } 141 142 public static ImmutableMap<String, String> detectOsProperties() { 143 return ImmutableMap.of( 144 "os.detected.name", 145 osDetectedName(), 146 "os.detected.arch", 147 osDetectedArch(), 148 "os.detected.classifier", 149 osDetectedName() + "-" + osDetectedArch()); 150 } 151 152 private static String osDetectedName() { 153 String osNameNormalized = 154 LOWER_ALPHA_NUMERIC.retainFrom(System.getProperty("os.name").toLowerCase(Locale.ENGLISH)); 155 156 if (osNameNormalized.startsWith("macosx") || osNameNormalized.startsWith("osx")) { 157 return "osx"; 158 } else if (osNameNormalized.startsWith("windows")) { 159 return "windows"; 160 } 161 // Since we only load the dependency graph, not actually use the 162 // dependency, it doesn't matter a great deal which one we pick. 163 return "linux"; 164 } 165 166 private static String osDetectedArch() { 167 String osArchNormalized = 168 LOWER_ALPHA_NUMERIC.retainFrom(System.getProperty("os.arch").toLowerCase(Locale.ENGLISH)); 169 switch (osArchNormalized) { 170 case "x8664": 171 case "amd64": 172 case "ia32e": 173 case "em64t": 174 case "x64": 175 return "x86_64"; 176 default: 177 return "x86_32"; 178 } 179 } 180}