View Javadoc
1   package org.apache.maven.shared.release.config;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.OutputStream;
29  import java.util.Map;
30  import java.util.Properties;
31  
32  import org.apache.maven.model.Scm;
33  import org.apache.maven.shared.release.config.ReleaseDescriptorBuilder.BuilderReleaseDescriptor;
34  import org.apache.maven.shared.release.scm.IdentifiedScm;
35  import org.codehaus.plexus.component.annotations.Component;
36  import org.codehaus.plexus.component.annotations.Requirement;
37  import org.codehaus.plexus.logging.AbstractLogEnabled;
38  import org.codehaus.plexus.util.StringUtils;
39  import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
40  import org.sonatype.plexus.components.cipher.PlexusCipherException;
41  import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
42  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
43  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
44  import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
45  import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
46  
47  /**
48   * Read and write release configuration and state from a properties file.
49   *
50   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
51   */
52  @Component( role = ReleaseDescriptorStore.class, hint = "properties" )
53  public class PropertiesReleaseDescriptorStore
54      extends AbstractLogEnabled
55      implements ReleaseDescriptorStore
56  {
57  
58      /**
59       * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be
60       * used instead.
61       */
62      @Requirement( role = SecDispatcher.class, hint = "mng-4384" )
63      private DefaultSecDispatcher secDispatcher;
64  
65      @Override
66      public ReleaseDescriptorBuilder read( ReleaseDescriptorBuilder mergeDescriptor )
67          throws ReleaseDescriptorStoreException
68      {
69          return read( mergeDescriptor, getDefaultReleasePropertiesFile( mergeDescriptor.build() ) );
70      }
71  
72      public ReleaseDescriptorBuilder read( File file )
73          throws ReleaseDescriptorStoreException
74      {
75          return read( null, file );
76      }
77  
78      public ReleaseDescriptorBuilder read( ReleaseDescriptorBuilder mergeDescriptor, File file )
79          throws ReleaseDescriptorStoreException
80      {
81          Properties properties = new Properties();
82  
83          try ( InputStream inStream = new FileInputStream( file ) )
84          {
85              properties.load( inStream );
86          }
87          catch ( FileNotFoundException e )
88          {
89              getLogger().debug( file.getName() + " not found - using empty properties" );
90          }
91          catch ( IOException e )
92          {
93              throw new ReleaseDescriptorStoreException(
94                  "Error reading properties file '" + file.getName() + "': " + e.getMessage(), e );
95          }
96  
97          ReleaseDescriptorBuilder builder;
98          if ( mergeDescriptor != null )
99          {
100             builder = mergeDescriptor;
101         }
102         else
103         {
104            builder = new ReleaseDescriptorBuilder(); 
105         }
106         
107         ReleaseUtils.copyPropertiesToReleaseDescriptor( properties, builder );
108 
109         return builder;
110     }
111 
112     @Override
113     public void write( ReleaseDescriptor config )
114         throws ReleaseDescriptorStoreException
115     {
116         write( (BuilderReleaseDescriptor) config, getDefaultReleasePropertiesFile( config ) );
117     }
118 
119     @Override
120     public void delete( ReleaseDescriptor config )
121     {
122         File file = getDefaultReleasePropertiesFile( config );
123         if ( file.exists() )
124         {
125             file.delete();
126         }
127     }
128 
129     public void write( BuilderReleaseDescriptor config, File file )
130         throws ReleaseDescriptorStoreException
131     {
132         Properties properties = new Properties();
133         properties.setProperty( "completedPhase", config.getCompletedPhase() );
134         if ( config.isCommitByProject() ) //default is false
135         {
136             properties.setProperty( "commitByProject", "true" );
137         }
138         properties.setProperty( "scm.url", config.getScmSourceUrl() );
139         if ( config.getScmId() != null )
140         {
141             properties.setProperty( "scm.id", config.getScmId() );
142         }
143         if ( config.getScmUsername() != null )
144         {
145             properties.setProperty( "scm.username", config.getScmUsername() );
146         }
147         if ( config.getScmPassword() != null )
148         {
149             String password = config.getScmPassword();
150             try
151             {
152                 password = encryptAndDecorate( password );
153             }
154             catch ( IllegalStateException | SecDispatcherException | PlexusCipherException e )
155             {
156                 getLogger().debug( e.getMessage() );
157             }
158             properties.setProperty( "scm.password", password );
159         }
160         if ( config.getScmPrivateKey() != null )
161         {
162             properties.setProperty( "scm.privateKey", config.getScmPrivateKey() );
163         }
164         if ( config.getScmPrivateKeyPassPhrase() != null )
165         {
166             String passPhrase = config.getScmPrivateKeyPassPhrase();
167             try
168             {
169                 passPhrase = encryptAndDecorate( passPhrase );
170             }
171             catch ( IllegalStateException | SecDispatcherException | PlexusCipherException e )
172             {
173                 getLogger().debug( e.getMessage() );
174             }
175             properties.setProperty( "scm.passphrase", passPhrase  );
176         }
177         if ( config.getScmTagBase() != null )
178         {
179             properties.setProperty( "scm.tagBase", config.getScmTagBase() );
180         }
181         if ( config.getScmBranchBase() != null )
182         {
183             properties.setProperty( "scm.branchBase", config.getScmBranchBase() );
184         }
185         if ( config.getScmReleaseLabel() != null )
186         {
187             properties.setProperty( "scm.tag", config.getScmReleaseLabel() );
188         }
189         if ( config.getScmTagNameFormat() != null )
190         {
191             properties.setProperty( "scm.tagNameFormat", config.getScmTagNameFormat() );
192         }
193         if ( config.getScmCommentPrefix() != null )
194         {
195             properties.setProperty( "scm.commentPrefix", config.getScmCommentPrefix() );
196         }
197         if ( config.getScmDevelopmentCommitComment() != null )
198         {
199             properties.setProperty( "scm.developmentCommitComment", config.getScmDevelopmentCommitComment() );
200         }
201         if ( config.getScmReleaseCommitComment() != null )
202         {
203             properties.setProperty( "scm.releaseCommitComment", config.getScmReleaseCommitComment() );
204         }
205         if ( config.getScmBranchCommitComment() != null )
206         {
207             properties.setProperty( "scm.branchCommitComment", config.getScmBranchCommitComment() );
208         }
209         if ( config.getScmRollbackCommitComment() != null )
210         {
211             properties.setProperty( "scm.rollbackCommitComment", config.getScmRollbackCommitComment() );
212         }
213         if ( config.getAdditionalArguments() != null )
214         {
215             properties.setProperty( "exec.additionalArguments", config.getAdditionalArguments() );
216         }
217         if ( config.getPomFileName() != null )
218         {
219             properties.setProperty( "exec.pomFileName", config.getPomFileName() );
220         }
221         if ( !config.getActivateProfiles().isEmpty() )
222         {
223             properties.setProperty( "exec.activateProfiles",
224                                     StringUtils.join( config.getActivateProfiles().iterator(), "," ) );
225         }
226         if ( config.getPreparationGoals() != null )
227         {
228             properties.setProperty( "preparationGoals", config.getPreparationGoals() );
229         }
230         if ( config.getCompletionGoals() != null )
231         {
232             properties.setProperty( "completionGoals", config.getCompletionGoals() );
233         }
234         if ( config.getProjectVersionPolicyId() != null )
235         {
236             properties.setProperty( "projectVersionPolicyId", config.getProjectVersionPolicyId() );
237         }
238         if ( config.getProjectNamingPolicyId() != null )
239         {
240             properties.setProperty( "projectNamingPolicyId", config.getProjectNamingPolicyId() );
241         }
242         if ( config.getReleaseStrategyId() != null )
243         {
244             properties.setProperty( "releaseStrategyId", config.getReleaseStrategyId() );
245         }
246 
247         properties.setProperty( "exec.snapshotReleasePluginAllowed",
248                                 Boolean.toString( config.isSnapshotReleasePluginAllowed() ) );
249 
250         properties.setProperty( "remoteTagging", Boolean.toString( config.isRemoteTagging() ) );
251 
252         properties.setProperty( "pushChanges", Boolean.toString( config.isPushChanges() ) );
253 
254         if ( config.getWorkItem() != null )
255         {
256             properties.setProperty( "workItem", config.getWorkItem() );
257         }
258 
259         // others boolean properties are not written to the properties file because the value from the caller is always
260         // used
261 
262         
263         for ( Map.Entry<String, ReleaseStageVersions> entry : config.getProjectVersions().entrySet() )
264         {
265             if ( entry.getValue().getRelease() != null )
266             {
267                 properties.setProperty( "project.rel." + entry.getKey(), entry.getValue().getRelease() );
268             }
269             if ( entry.getValue().getDevelopment() != null )
270             {
271                 properties.setProperty( "project.dev." + entry.getKey(), entry.getValue().getDevelopment() );
272             }
273         }
274 
275         for ( Map.Entry<String, Scm> entry : config.getOriginalScmInfo().entrySet() )
276         {
277             Scm scm = entry.getValue();
278             String prefix = "project.scm." + entry.getKey();
279             if ( scm != null )
280             {
281                 if ( scm.getConnection() != null )
282                 {
283                     properties.setProperty( prefix + ".connection", scm.getConnection() );
284                 }
285                 if ( scm.getDeveloperConnection() != null )
286                 {
287                     properties.setProperty( prefix + ".developerConnection", scm.getDeveloperConnection() );
288                 }
289                 if ( scm.getUrl() != null )
290                 {
291                     properties.setProperty( prefix + ".url", scm.getUrl() );
292                 }
293                 if ( scm.getTag() != null )
294                 {
295                     properties.setProperty( prefix + ".tag", scm.getTag() );
296                 }
297                 if ( scm instanceof IdentifiedScm )
298                 {
299                     IdentifiedScm identifiedScm = (IdentifiedScm) scm;
300                     if ( identifiedScm.getId() != null )
301                     {
302                         properties.setProperty( prefix + ".id", identifiedScm.getId() );
303                     }
304                 }
305             }
306             else
307             {
308                 properties.setProperty( prefix + ".empty", "true" );
309             }
310         }
311 
312         if ( ( config.getResolvedSnapshotDependencies() != null )
313             && ( config.getResolvedSnapshotDependencies().size() > 0 ) )
314         {
315             processResolvedDependencies( properties, config.getResolvedSnapshotDependencies() );
316         }
317 
318         try ( OutputStream outStream = new FileOutputStream( file ) )
319         {
320             properties.store( outStream, "release configuration" );
321         }
322         catch ( IOException e )
323         {
324             throw new ReleaseDescriptorStoreException(
325                 "Error writing properties file '" + file.getName() + "': " + e.getMessage(), e );
326         }
327     }
328 
329     private void processResolvedDependencies( Properties prop, Map<String, ReleaseStageVersions> resolvedDependencies )
330     {
331         for ( Map.Entry<String, ReleaseStageVersions> currentEntry : resolvedDependencies.entrySet() )
332         {
333             ReleaseStageVersions versionMap = currentEntry.getValue();
334             
335             prop.setProperty( "dependency." + currentEntry.getKey() + ".release",
336                               versionMap.getRelease() );
337             prop.setProperty( "dependency." + currentEntry.getKey() + ".development",
338                               versionMap.getDevelopment() );
339         }
340     }
341 
342     private static File getDefaultReleasePropertiesFile( ReleaseDescriptor mergeDescriptor )
343     {
344         return new File( mergeDescriptor.getWorkingDirectory(), "release.properties" );
345     }
346 
347     // From org.apache.maven.cli.MavenCli.encryption(CliRequest)
348     private String encryptAndDecorate( String passwd )
349         throws IllegalStateException, SecDispatcherException, PlexusCipherException
350     {
351         String configurationFile = secDispatcher.getConfigurationFile();
352 
353         if ( configurationFile.startsWith( "~" ) )
354         {
355             configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
356         }
357 
358         String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
359 
360         String master = null;
361 
362         SettingsSecurity sec = SecUtil.read( file, true );
363         if ( sec != null )
364         {
365             master = sec.getMaster();
366         }
367 
368         if ( master == null )
369         {
370             throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
371         }
372 
373         DefaultPlexusCipher cipher = new DefaultPlexusCipher();
374         String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
375         return cipher.encryptAndDecorate( passwd, masterPasswd );
376     }
377 
378 }