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.shared.release.config;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileNotFoundException;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.OutputStream;
32  import java.util.Map;
33  import java.util.Properties;
34  
35  import org.apache.maven.model.Scm;
36  import org.apache.maven.shared.release.config.ReleaseDescriptorBuilder.BuilderReleaseDescriptor;
37  import org.apache.maven.shared.release.scm.IdentifiedScm;
38  import org.apache.maven.shared.release.util.MavenCrypto;
39  import org.apache.maven.shared.release.util.MavenCrypto.MavenCryptoException;
40  import org.codehaus.plexus.util.StringUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import static java.util.Objects.requireNonNull;
45  
46  /**
47   * Read and write release configuration and state from a properties file.
48   *
49   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
50   */
51  @Singleton
52  @Named("properties")
53  public class PropertiesReleaseDescriptorStore implements ReleaseDescriptorStore {
54      private final Logger logger = LoggerFactory.getLogger(getClass());
55  
56      private final MavenCrypto mavenCrypto;
57  
58      @Inject
59      public PropertiesReleaseDescriptorStore(MavenCrypto mavenCrypto) {
60          this.mavenCrypto = requireNonNull(mavenCrypto);
61      }
62  
63      @Override
64      public ReleaseDescriptorBuilder read(ReleaseDescriptorBuilder mergeDescriptor)
65              throws ReleaseDescriptorStoreException {
66          return read(mergeDescriptor, getDefaultReleasePropertiesFile(mergeDescriptor.build()));
67      }
68  
69      /**
70       * <p>read.</p>
71       *
72       * @param file a {@link java.io.File} object
73       * @return a {@link org.apache.maven.shared.release.config.ReleaseDescriptorBuilder} object
74       * @throws org.apache.maven.shared.release.config.ReleaseDescriptorStoreException if any.
75       */
76      public ReleaseDescriptorBuilder read(File file) throws ReleaseDescriptorStoreException {
77          return read(null, file);
78      }
79  
80      /**
81       * <p>read.</p>
82       *
83       * @param mergeDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptorBuilder} object
84       * @param file            a {@link java.io.File} object
85       * @return a {@link org.apache.maven.shared.release.config.ReleaseDescriptorBuilder} object
86       * @throws org.apache.maven.shared.release.config.ReleaseDescriptorStoreException if any.
87       */
88      public ReleaseDescriptorBuilder read(ReleaseDescriptorBuilder mergeDescriptor, File file)
89              throws ReleaseDescriptorStoreException {
90          Properties properties = new Properties();
91  
92          try (InputStream inStream = new FileInputStream(file)) {
93              properties.load(inStream);
94          } catch (FileNotFoundException e) {
95              logger.debug(file.getName() + " not found - using empty properties");
96          } catch (IOException e) {
97              throw new ReleaseDescriptorStoreException(
98                      "Error reading properties file '" + file.getName() + "': " + e.getMessage(), e);
99          }
100 
101         try {
102             mavenCrypto.decryptProperties(properties);
103         } catch (MavenCryptoException e) {
104             logger.debug(e.getMessage());
105         }
106 
107         ReleaseDescriptorBuilder builder;
108         if (mergeDescriptor != null) {
109             builder = mergeDescriptor;
110         } else {
111             builder = new ReleaseDescriptorBuilder();
112         }
113 
114         ReleaseUtils.copyPropertiesToReleaseDescriptor(properties, builder);
115 
116         return builder;
117     }
118 
119     @Override
120     public void write(ReleaseDescriptor config) throws ReleaseDescriptorStoreException {
121         write((BuilderReleaseDescriptor) config, getDefaultReleasePropertiesFile(config));
122     }
123 
124     @Override
125     public void delete(ReleaseDescriptor config) {
126         File file = getDefaultReleasePropertiesFile(config);
127         if (file.exists()) {
128             file.delete();
129         }
130     }
131 
132     /**
133      * <p>write.</p>
134      *
135      * @param config a {@link org.apache.maven.shared.release.config.ReleaseDescriptorBuilder.BuilderReleaseDescriptor}
136      *               object
137      * @param file   a {@link java.io.File} object
138      * @throws org.apache.maven.shared.release.config.ReleaseDescriptorStoreException if any.
139      */
140     @SuppressWarnings("checkstyle:MethodLength")
141     public void write(BuilderReleaseDescriptor config, File file) throws ReleaseDescriptorStoreException {
142         Properties properties = new Properties();
143         properties.setProperty("completedPhase", config.getCompletedPhase());
144         if (config.isCommitByProject()) // default is false
145         {
146             properties.setProperty("commitByProject", "true");
147         }
148         properties.setProperty("scm.url", config.getScmSourceUrl());
149         if (config.getScmId() != null) {
150             properties.setProperty("scm.id", config.getScmId());
151         }
152         if (config.getScmUsername() != null) {
153             properties.setProperty("scm.username", config.getScmUsername());
154         }
155         if (config.getScmPassword() != null) {
156             String password = config.getScmPassword();
157             try {
158                 password = mavenCrypto.encryptAndDecorate(password);
159             } catch (MavenCryptoException e) {
160                 logger.debug(e.getMessage());
161             }
162             properties.setProperty("scm.password", password);
163         }
164         if (config.getScmPrivateKey() != null) {
165             properties.setProperty("scm.privateKey", config.getScmPrivateKey());
166         }
167         if (config.getScmPrivateKeyPassPhrase() != null) {
168             String passPhrase = config.getScmPrivateKeyPassPhrase();
169             try {
170                 passPhrase = mavenCrypto.encryptAndDecorate(passPhrase);
171             } catch (MavenCryptoException e) {
172                 logger.debug(e.getMessage());
173             }
174             properties.setProperty("scm.passphrase", passPhrase);
175         }
176         if (config.getScmTagBase() != null) {
177             properties.setProperty("scm.tagBase", config.getScmTagBase());
178         }
179         if (config.getScmBranchBase() != null) {
180             properties.setProperty("scm.branchBase", config.getScmBranchBase());
181         }
182         if (config.getScmReleaseLabel() != null) {
183             properties.setProperty("scm.tag", config.getScmReleaseLabel());
184         }
185         if (config.getScmTagNameFormat() != null) {
186             properties.setProperty("scm.tagNameFormat", config.getScmTagNameFormat());
187         }
188         if (config.getScmCommentPrefix() != null) {
189             properties.setProperty("scm.commentPrefix", config.getScmCommentPrefix());
190         }
191         if (config.getScmDevelopmentCommitComment() != null) {
192             properties.setProperty("scm.developmentCommitComment", config.getScmDevelopmentCommitComment());
193         }
194         if (config.getScmReleaseCommitComment() != null) {
195             properties.setProperty("scm.releaseCommitComment", config.getScmReleaseCommitComment());
196         }
197         if (config.getScmBranchCommitComment() != null) {
198             properties.setProperty("scm.branchCommitComment", config.getScmBranchCommitComment());
199         }
200         if (config.getScmRollbackCommitComment() != null) {
201             properties.setProperty("scm.rollbackCommitComment", config.getScmRollbackCommitComment());
202         }
203         if (config.getAdditionalArguments() != null) {
204             properties.setProperty("exec.additionalArguments", config.getAdditionalArguments());
205         }
206         if (config.getPomFileName() != null) {
207             properties.setProperty("exec.pomFileName", config.getPomFileName());
208         }
209         if (!config.getActivateProfiles().isEmpty()) {
210             properties.setProperty(
211                     "exec.activateProfiles",
212                     StringUtils.join(config.getActivateProfiles().iterator(), ","));
213         }
214         if (config.getPreparationGoals() != null) {
215             properties.setProperty("preparationGoals", config.getPreparationGoals());
216         }
217         if (config.getCompletionGoals() != null) {
218             properties.setProperty("completionGoals", config.getCompletionGoals());
219         }
220         if (config.getProjectVersionPolicyId() != null) {
221             properties.setProperty("projectVersionPolicyId", config.getProjectVersionPolicyId());
222         }
223         if (config.getProjectVersionPolicyConfig() != null) {
224             properties.setProperty(
225                     "projectVersionPolicyConfig",
226                     config.getProjectVersionPolicyConfig().toString());
227         }
228         if (config.getProjectNamingPolicyId() != null) {
229             properties.setProperty("projectNamingPolicyId", config.getProjectNamingPolicyId());
230         }
231         if (config.getReleaseStrategyId() != null) {
232             properties.setProperty("releaseStrategyId", config.getReleaseStrategyId());
233         }
234 
235         properties.setProperty(
236                 "exec.snapshotReleasePluginAllowed", Boolean.toString(config.isSnapshotReleasePluginAllowed()));
237 
238         properties.setProperty("remoteTagging", Boolean.toString(config.isRemoteTagging()));
239 
240         properties.setProperty("pinExternals", Boolean.toString(config.isPinExternals()));
241 
242         properties.setProperty("pushChanges", Boolean.toString(config.isPushChanges()));
243 
244         if (config.getWorkItem() != null) {
245             properties.setProperty("workItem", config.getWorkItem());
246         }
247 
248         if (config.getAutoResolveSnapshots() != null) {
249             properties.setProperty("autoResolveSnapshots", config.getAutoResolveSnapshots());
250         }
251 
252         // others boolean properties are not written to the properties file because the value from the caller is always
253         // used
254 
255         for (Map.Entry<String, ReleaseStageVersions> entry :
256                 config.getProjectVersions().entrySet()) {
257             if (entry.getValue().getRelease() != null) {
258                 properties.setProperty(
259                         "project.rel." + entry.getKey(), entry.getValue().getRelease());
260             }
261             if (entry.getValue().getDevelopment() != null) {
262                 properties.setProperty(
263                         "project.dev." + entry.getKey(), entry.getValue().getDevelopment());
264             }
265         }
266 
267         for (Map.Entry<String, Scm> entry : config.getOriginalScmInfo().entrySet()) {
268             Scm scm = entry.getValue();
269             String prefix = "project.scm." + entry.getKey();
270             if (scm != null) {
271                 if (scm.getConnection() != null) {
272                     properties.setProperty(prefix + ".connection", scm.getConnection());
273                 }
274                 if (scm.getDeveloperConnection() != null) {
275                     properties.setProperty(prefix + ".developerConnection", scm.getDeveloperConnection());
276                 }
277                 if (scm.getUrl() != null) {
278                     properties.setProperty(prefix + ".url", scm.getUrl());
279                 }
280                 if (scm.getTag() != null) {
281                     properties.setProperty(prefix + ".tag", scm.getTag());
282                 }
283                 if (scm instanceof IdentifiedScm) {
284                     IdentifiedScm identifiedScm = (IdentifiedScm) scm;
285                     if (identifiedScm.getId() != null) {
286                         properties.setProperty(prefix + ".id", identifiedScm.getId());
287                     }
288                 }
289             } else {
290                 properties.setProperty(prefix + ".empty", "true");
291             }
292         }
293 
294         if ((config.getResolvedSnapshotDependencies() != null)
295                 && (config.getResolvedSnapshotDependencies().size() > 0)) {
296             processResolvedDependencies(properties, config.getResolvedSnapshotDependencies());
297         }
298 
299         try (OutputStream outStream = new FileOutputStream(file)) {
300             properties.store(outStream, "release configuration");
301         } catch (IOException e) {
302             throw new ReleaseDescriptorStoreException(
303                     "Error writing properties file '" + file.getName() + "': " + e.getMessage(), e);
304         }
305     }
306 
307     private void processResolvedDependencies(Properties prop, Map<String, ReleaseStageVersions> resolvedDependencies) {
308         for (Map.Entry<String, ReleaseStageVersions> currentEntry : resolvedDependencies.entrySet()) {
309             ReleaseStageVersions versionMap = currentEntry.getValue();
310 
311             prop.setProperty("dependency." + currentEntry.getKey() + ".release", versionMap.getRelease());
312             prop.setProperty("dependency." + currentEntry.getKey() + ".development", versionMap.getDevelopment());
313         }
314     }
315 
316     private static File getDefaultReleasePropertiesFile(ReleaseDescriptor mergeDescriptor) {
317         return new File(mergeDescriptor.getWorkingDirectory(), "release.properties");
318     }
319 }