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.Iterator;
30  import java.util.Map;
31  import java.util.Map.Entry;
32  import java.util.Properties;
33  import java.util.Set;
34  
35  import org.apache.maven.model.Scm;
36  import org.apache.maven.shared.release.scm.IdentifiedScm;
37  import org.codehaus.plexus.logging.AbstractLogEnabled;
38  import org.codehaus.plexus.util.IOUtil;
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.SecDispatcherException;
43  import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
44  import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
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   * @plexus.component role="org.apache.maven.shared.release.config.ReleaseDescriptorStore" role-hint="properties"
51   */
52  public class PropertiesReleaseDescriptorStore
53      extends AbstractLogEnabled
54      implements ReleaseDescriptorStore
55  {
56  
57      /**
58       * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be
59       * used instead.
60       *
61       * @plexus.requirement role="org.sonatype.plexus.components.sec.dispatcher.SecDispatcher" role-hint="mng-4384"
62       */
63  
64      private DefaultSecDispatcher secDispatcher;
65  
66      public ReleaseDescriptor read( ReleaseDescriptor mergeDescriptor )
67          throws ReleaseDescriptorStoreException
68      {
69          return read( mergeDescriptor, getDefaultReleasePropertiesFile( mergeDescriptor ) );
70      }
71  
72      public ReleaseDescriptor read( File file )
73          throws ReleaseDescriptorStoreException
74      {
75          return read( null, file );
76      }
77  
78      public ReleaseDescriptor read( ReleaseDescriptor mergeDescriptor, File file )
79          throws ReleaseDescriptorStoreException
80      {
81          Properties properties = new Properties();
82  
83          InputStream inStream = null;
84          try
85          {
86              inStream = new FileInputStream( file );
87  
88              properties.load( inStream );
89          }
90          catch ( FileNotFoundException e )
91          {
92              getLogger().debug( file.getName() + " not found - using empty properties" );
93          }
94          catch ( IOException e )
95          {
96              throw new ReleaseDescriptorStoreException(
97                  "Error reading properties file '" + file.getName() + "': " + e.getMessage(), e );
98          }
99          finally
100         {
101             IOUtil.close( inStream );
102         }
103 
104         ReleaseDescriptor releaseDescriptor = ReleaseUtils.copyPropertiesToReleaseDescriptor( properties );
105 
106         if ( mergeDescriptor != null )
107         {
108             releaseDescriptor = ReleaseUtils.merge( releaseDescriptor, mergeDescriptor );
109         }
110 
111         return releaseDescriptor;
112     }
113 
114     public void write( ReleaseDescriptor config )
115         throws ReleaseDescriptorStoreException
116     {
117         write( config, getDefaultReleasePropertiesFile( config ) );
118     }
119 
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( ReleaseDescriptor 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 e )
155             {
156                 getLogger().debug( e.getMessage() );
157             }
158             catch ( SecDispatcherException e )
159             {
160                 getLogger().debug( e.getMessage() );
161             }
162             catch ( PlexusCipherException e )
163             {
164                 getLogger().debug( e.getMessage() );
165             }
166             properties.setProperty( "scm.password", password );
167         }
168         if ( config.getScmPrivateKey() != null )
169         {
170             properties.setProperty( "scm.privateKey", config.getScmPrivateKey() );
171         }
172         if ( config.getScmPrivateKeyPassPhrase() != null )
173         {
174             String passPhrase = config.getScmPrivateKeyPassPhrase();
175             try
176             {
177                 passPhrase = encryptAndDecorate( passPhrase );
178             }
179             catch ( IllegalStateException e )
180             {
181                 getLogger().debug( e.getMessage() );
182             }
183             catch ( SecDispatcherException e )
184             {
185                 getLogger().debug( e.getMessage() );
186             }
187             catch ( PlexusCipherException e )
188             {
189                 getLogger().debug( e.getMessage() );
190             }
191             properties.setProperty( "scm.passphrase", passPhrase  );
192         }
193         if ( config.getScmTagBase() != null )
194         {
195             properties.setProperty( "scm.tagBase", config.getScmTagBase() );
196         }
197         if ( config.getScmBranchBase() != null )
198         {
199             properties.setProperty( "scm.branchBase", config.getScmBranchBase() );
200         }
201         if ( config.getScmReleaseLabel() != null )
202         {
203             properties.setProperty( "scm.tag", config.getScmReleaseLabel() );
204         }
205         if ( config.getScmTagNameFormat() != null )
206         {
207             properties.setProperty( "scm.tagNameFormat", config.getScmTagNameFormat() );
208         }
209         if ( config.getScmCommentPrefix() != null )
210         {
211             properties.setProperty( "scm.commentPrefix", config.getScmCommentPrefix() );
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.getPreparationGoals() != null )
222         {
223             properties.setProperty( "preparationGoals", config.getPreparationGoals() );
224         }
225         if ( config.getCompletionGoals() != null )
226         {
227             properties.setProperty( "completionGoals", config.getCompletionGoals() );
228         }
229         if ( config.getProjectVersionPolicyId() != null )
230         {
231             properties.setProperty( "projectVersionPolicyId", config.getProjectVersionPolicyId() );
232         }
233 
234         properties.setProperty( "exec.snapshotReleasePluginAllowed",
235                                 Boolean.toString( config.isSnapshotReleasePluginAllowed() ) );
236 
237         properties.setProperty( "remoteTagging", Boolean.toString( config.isRemoteTagging() ) );
238 
239         properties.setProperty( "pushChanges", Boolean.toString( config.isPushChanges() ) );
240 
241         // others boolean properties are not written to the properties file because the value from the caller is always
242         // used
243 
244         for ( Iterator<?> i = config.getReleaseVersions().entrySet().iterator(); i.hasNext(); )
245         {
246             Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
247             properties.setProperty( "project.rel." + entry.getKey(), (String) entry.getValue() );
248         }
249 
250         for ( Iterator<?> i = config.getDevelopmentVersions().entrySet().iterator(); i.hasNext(); )
251         {
252             Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
253             properties.setProperty( "project.dev." + entry.getKey(), (String) entry.getValue() );
254         }
255 
256         for ( Iterator<?> i = config.getOriginalScmInfo().entrySet().iterator(); i.hasNext(); )
257         {
258             Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
259             Scm scm = (Scm) entry.getValue();
260             String prefix = "project.scm." + entry.getKey();
261             if ( scm != null )
262             {
263                 if ( scm.getConnection() != null )
264                 {
265                     properties.setProperty( prefix + ".connection", scm.getConnection() );
266                 }
267                 if ( scm.getDeveloperConnection() != null )
268                 {
269                     properties.setProperty( prefix + ".developerConnection", scm.getDeveloperConnection() );
270                 }
271                 if ( scm.getUrl() != null )
272                 {
273                     properties.setProperty( prefix + ".url", scm.getUrl() );
274                 }
275                 if ( scm.getTag() != null )
276                 {
277                     properties.setProperty( prefix + ".tag", scm.getTag() );
278                 }
279                 if ( scm instanceof IdentifiedScm )
280                 {
281                     IdentifiedScm identifiedScm = (IdentifiedScm) scm;
282                     if ( identifiedScm.getId() != null )
283                     {
284                         properties.setProperty( prefix + ".id", identifiedScm.getId() );
285                     }
286                 }
287             }
288             else
289             {
290                 properties.setProperty( prefix + ".empty", "true" );
291             }
292         }
293 
294         if ( ( config.getResolvedSnapshotDependencies() != null )
295             && ( config.getResolvedSnapshotDependencies().size() > 0 ) )
296         {
297             processResolvedDependencies( properties, config.getResolvedSnapshotDependencies() );
298         }
299 
300         OutputStream outStream = null;
301         //noinspection OverlyBroadCatchBlock
302         try
303         {
304             outStream = new FileOutputStream( file );
305 
306             properties.store( outStream, "release configuration" );
307         }
308         catch ( IOException e )
309         {
310             throw new ReleaseDescriptorStoreException(
311                 "Error writing properties file '" + file.getName() + "': " + e.getMessage(), e );
312         }
313         finally
314         {
315             IOUtil.close( outStream );
316         }
317 
318     }
319 
320     private void processResolvedDependencies( Properties prop, Map<?, ?> resolvedDependencies )
321     {
322         Set<?> entries = resolvedDependencies.entrySet();
323         Iterator<?> iterator = entries.iterator();
324         Entry<?, ?> currentEntry;
325 
326         while ( iterator.hasNext() )
327         {
328             currentEntry = (Entry<?, ?>) iterator.next();
329 
330             Map<?, ?> versionMap = (Map<?, ?>) currentEntry.getValue();
331 
332             prop.setProperty( "dependency." + currentEntry.getKey() + ".release",
333                               (String) versionMap.get( ReleaseDescriptor.RELEASE_KEY ) );
334             prop.setProperty( "dependency." + currentEntry.getKey() + ".development",
335                               (String) versionMap.get( ReleaseDescriptor.DEVELOPMENT_KEY ) );
336         }
337     }
338 
339     private static File getDefaultReleasePropertiesFile( ReleaseDescriptor mergeDescriptor )
340     {
341         return new File( mergeDescriptor.getWorkingDirectory(), "release.properties" );
342     }
343 
344     // From org.apache.maven.cli.MavenCli.encryption(CliRequest)
345     private String encryptAndDecorate( String passwd )
346         throws IllegalStateException, SecDispatcherException, PlexusCipherException
347     {
348         String configurationFile = secDispatcher.getConfigurationFile();
349 
350         if ( configurationFile.startsWith( "~" ) )
351         {
352             configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
353         }
354 
355         String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
356 
357         String master = null;
358 
359         SettingsSecurity sec = SecUtil.read( file, true );
360         if ( sec != null )
361         {
362             master = sec.getMaster();
363         }
364 
365         if ( master == null )
366         {
367             throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
368         }
369 
370         DefaultPlexusCipher cipher = new DefaultPlexusCipher();
371         String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
372         return cipher.encryptAndDecorate( passwd, masterPasswd );
373     }
374 
375 }