001/* 002 * JDrupes Builder 003 * Copyright (C) 2026 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.builder.mvnrepo; 020 021import com.google.common.flogger.FluentLogger; 022import java.util.Arrays; 023import java.util.EnumSet; 024import java.util.List; 025import java.util.Set; 026import org.eclipse.aether.artifact.Artifact; 027import org.jdrupes.builder.api.BuildContext; 028 029/// The base class for all Maven publishing destinations. 030/// It provides common functionality for managing a destination's accepted 031/// version types (SNAPSHOT, RELEASE) and for managing repository credentials. 032/// 033/// Credentials for the destination are resolved using the following 034/// order of precedence: 035/// 036/// 1. Explicitly set credentials: Values provided via the [credentials] 037/// method take the highest priority. 038/// 2. Build context properties: If no explicit credentials are set, the 039/// publisher looks for `mvnrepo.user` and `mvnrepo.password` within 040/// the [BuildContext]. 041/// 3. Maven `settings.xml`: If an ID is configured for the destination, 042/// the publisher searches the `servers` section of the Maven 043/// `settings.xml` for a server matching that ID. 044/// 4. Fallback: If no credentials are found in any of the above sources, 045/// empty strings are used, which typically results in an anonymous 046/// upload attempt. 047/// 048public abstract class MvnPublishingDestination { 049 050 private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 051 private final Set<MvnVersionType> acceptedTypes; 052 private String repoUser; 053 private String repoPass; 054 private String id; 055 056 /// Initializes a new Maven publishing destination. 057 /// 058 /// @param publicationTypes the accepted publication types 059 /// 060 public MvnPublishingDestination(MvnVersionType... publicationTypes) { 061 acceptedTypes = EnumSet.copyOf(Arrays.asList(publicationTypes)); 062 } 063 064 /// Checks if the given publication type is accepted. 065 /// 066 /// @param type the type 067 /// @return true, if successful 068 /// 069 public boolean accepts(MvnVersionType type) { 070 return acceptedTypes.contains(type); 071 } 072 073 /// Returns if this destination requires checksum artifacts to 074 /// be passed to [publish]. 075 /// 076 /// @return true, if successful 077 /// 078 public boolean requiresChecksumArtifacts() { 079 return false; 080 } 081 082 /// Sets the id. 083 /// 084 /// @param id the new id 085 /// @return this destination 086 /// 087 @SuppressWarnings("PMD.ShortMethodName") 088 public MvnPublishingDestination id(String id) { 089 this.id = id; 090 return this; 091 } 092 093 /// 094 /// Returns the id. 095 /// 096 /// @return the id 097 /// 098 @SuppressWarnings("PMD.ShortMethodName") 099 public String id() { 100 return id; 101 } 102 103 /// Sets the Maven repository credentials. 104 /// 105 /// @param user the user name 106 /// @param pass the password 107 /// @return this destination 108 /// 109 public MvnPublishingDestination credentials(String user, String pass) { 110 logger.atConfig().log("Using explicitly set credentials for %s", this); 111 this.repoUser = user; 112 this.repoPass = pass; 113 return this; 114 } 115 116 /// Returns the repository user set by [credentials] or a fallback. 117 /// 118 /// The fallback order is: 119 /// 120 /// 1. Look for properties `mvnrepo.user` and `mvnrepo.password` 121 /// in the properties provided by the [BuildContext]. 122 /// 123 /// 2. If an id is set, look for the user and password in the 124 /// `servers` section with this id in the Maven `settings.xml`. 125 /// 126 /// @param context the context 127 /// @return the user 128 /// 129 protected String repositoryUser(BuildContext context) { 130 fillInCredentials(context); 131 return repoUser; 132 } 133 134 /// Returns the repository password set by [credentials] or a fallback. 135 /// See [repositoryUser] for the fallback logic. 136 /// 137 /// @param context the context 138 /// @return the password 139 /// 140 protected String repositoryPassword(BuildContext context) { 141 fillInCredentials(context); 142 return repoPass; 143 } 144 145 @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") 146 private synchronized void fillInCredentials(BuildContext context) { 147 if (repoUser != null) { 148 return; 149 } 150 151 // Try properties 152 var user = context.property("mvnrepo.user", null); 153 if (user != null) { 154 logger.atConfig().log( 155 "Using credentials from properties for %s", this); 156 repoUser = user; 157 repoPass = context.property("mvnrepo.password", null); 158 return; 159 } 160 161 // Try settings 162 if (id != null && MavenContext.lookupCredentials(id, (u, p) -> { 163 logger.atConfig().log( 164 "Using credentials from settings for %s", this); 165 repoUser = u; 166 repoPass = p; 167 })) { 168 return; 169 } 170 171 // Fallback 172 repoUser = ""; 173 repoPass = ""; 174 } 175 176 /* default */ abstract void publish(BuildContext context, 177 MvnPublisher publisher, Artifact mainArtifact, 178 List<Artifact> toDeploy); 179}