View Javadoc

1   package org.apache.maven.continuum.release;
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 org.apache.commons.lang.BooleanUtils;
23  import org.apache.continuum.model.release.ReleaseListenerSummary;
24  import org.apache.continuum.model.repository.LocalRepository;
25  import org.apache.continuum.release.config.ContinuumReleaseDescriptor;
26  import org.apache.continuum.taskqueue.manager.TaskQueueManagerException;
27  import org.apache.maven.artifact.ArtifactUtils;
28  import org.apache.maven.continuum.model.project.Project;
29  import org.apache.maven.continuum.release.tasks.PerformReleaseProjectTask;
30  import org.apache.maven.continuum.release.tasks.PrepareReleaseProjectTask;
31  import org.apache.maven.continuum.release.tasks.RollbackReleaseProjectTask;
32  import org.apache.maven.scm.manager.ScmManager;
33  import org.apache.maven.scm.provider.ScmProvider;
34  import org.apache.maven.scm.repository.ScmRepository;
35  import org.apache.maven.shared.release.ReleaseManagerListener;
36  import org.apache.maven.shared.release.config.ReleaseDescriptor;
37  import org.apache.maven.shared.release.config.ReleaseDescriptorStore;
38  import org.apache.maven.shared.release.config.ReleaseDescriptorStoreException;
39  import org.codehaus.plexus.PlexusConstants;
40  import org.codehaus.plexus.PlexusContainer;
41  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
42  import org.codehaus.plexus.context.Context;
43  import org.codehaus.plexus.context.ContextException;
44  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
45  import org.codehaus.plexus.taskqueue.Task;
46  import org.codehaus.plexus.taskqueue.TaskQueue;
47  import org.codehaus.plexus.taskqueue.TaskQueueException;
48  import org.codehaus.plexus.taskqueue.execution.TaskQueueExecutor;
49  import org.codehaus.plexus.util.StringUtils;
50  
51  import java.io.File;
52  import java.util.Collections;
53  import java.util.Hashtable;
54  import java.util.LinkedHashMap;
55  import java.util.Map;
56  import java.util.Properties;
57  
58  /**
59   * @author Jason van Zyl
60   * @author Edwin Punzalan
61   * @version $Id: DefaultContinuumReleaseManager.java 1392719 2012-10-02 03:17:07Z brett $
62   */
63  public class DefaultContinuumReleaseManager
64      implements ContinuumReleaseManager, Contextualizable
65  {
66  
67      private static final String PLEXUS_KEY_PERFORM_RELEASE_TASKQUEUE_EXECUTOR = "perform-release";
68  
69      private static final String PLEXUS_KEY_PREPARE_RELEASE_TASKQUEUE_EXECUTOR = "prepare-release";
70  
71      private static final String PLEXUS_KEY_ROLLBACK_RELEASE_TASKQUEUE_EXECUTOR = "rollback-release";
72  
73      /**
74       * @plexus.requirement
75       */
76      private ReleaseDescriptorStore releaseStore;
77  
78      /**
79       * @plexus.requirement
80       */
81      private TaskQueue prepareReleaseQueue;
82  
83      /**
84       * @plexus.requirement
85       */
86      private TaskQueue performReleaseQueue;
87  
88      /**
89       * @plexus.requirement
90       */
91      private TaskQueue rollbackReleaseQueue;
92  
93      /**
94       * @plexus.requirement
95       */
96      private ScmManager scmManager;
97  
98      private PlexusContainer container;
99  
100     private Map<String, ContinuumReleaseManagerListener> listeners;
101 
102     /**
103      * contains previous release:prepare descriptors; one per project
104      *
105      * @todo remove static when singleton strategy is working
106      */
107     private static Map<String, ReleaseDescriptor> preparedReleases;
108 
109     /**
110      * contains results
111      *
112      * @todo remove static when singleton strategy is working
113      */
114     private static Map releaseResults;
115 
116     public String prepare( Project project, Properties releaseProperties, Map<String, String> relVersions,
117                            Map<String, String> devVersions, ContinuumReleaseManagerListener listener,
118                            String workingDirectory )
119         throws ContinuumReleaseException
120     {
121         return prepare( project, releaseProperties, relVersions, devVersions, listener, workingDirectory, null, null );
122     }
123 
124     public String prepare( Project project, Properties releaseProperties, Map<String, String> relVersions,
125                            Map<String, String> devVersions, ContinuumReleaseManagerListener listener,
126                            String workingDirectory, Map<String, String> environments, String executable )
127         throws ContinuumReleaseException
128     {
129         String releaseId = project.getGroupId() + ":" + project.getArtifactId();
130 
131         ReleaseDescriptor descriptor = getReleaseDescriptor( project, releaseProperties, relVersions, devVersions,
132                                                              environments, workingDirectory, executable );
133 
134         if ( listener == null )
135         {
136             listener = new DefaultReleaseManagerListener();
137             listener.setUsername( releaseProperties.getProperty( "release-by" ) );
138         }
139 
140         // check if releaseId exists
141         while ( getPreparedReleases().get( releaseId ) != null )
142         {
143             releaseId = releaseId + ":" + String.valueOf( System.currentTimeMillis() );
144         }
145 
146         getListeners().put( releaseId, listener );
147 
148         try
149         {
150             prepareReleaseQueue.put( new PrepareReleaseProjectTask( releaseId, descriptor,
151                                                                     (ReleaseManagerListener) listener ) );
152         }
153         catch ( TaskQueueException e )
154         {
155             throw new ContinuumReleaseException( "Failed to add prepare release task in queue.", e );
156         }
157 
158         return releaseId;
159     }
160 
161     public void perform( String releaseId, File buildDirectory, String goals, String arguments,
162                          boolean useReleaseProfile, ContinuumReleaseManagerListener listener )
163         throws ContinuumReleaseException
164     {
165         perform( releaseId, buildDirectory, goals, arguments, useReleaseProfile, listener, null );
166     }
167 
168     public void perform( String releaseId, File buildDirectory, String goals, String arguments,
169                          boolean useReleaseProfile, ContinuumReleaseManagerListener listener,
170                          LocalRepository repository )
171         throws ContinuumReleaseException
172     {
173         ReleaseDescriptor descriptor = getPreparedReleases().get( releaseId );
174         if ( descriptor != null )
175         {
176             perform( releaseId, descriptor, buildDirectory, goals, arguments, useReleaseProfile, listener, repository );
177         }
178     }
179 
180     public void perform( String releaseId, String workingDirectory, File buildDirectory, String goals, String arguments,
181                          boolean useReleaseProfile, ContinuumReleaseManagerListener listener )
182         throws ContinuumReleaseException
183     {
184         ReleaseDescriptor descriptor = readReleaseDescriptor( workingDirectory );
185         perform( releaseId, descriptor, buildDirectory, goals, arguments, useReleaseProfile, listener, null );
186     }
187 
188     private void perform( String releaseId, ReleaseDescriptor descriptor, File buildDirectory, String goals,
189                           String arguments, boolean useReleaseProfile, ContinuumReleaseManagerListener listener,
190                           LocalRepository repository )
191         throws ContinuumReleaseException
192     {
193         if ( descriptor != null )
194         {
195             descriptor.setAdditionalArguments( arguments );
196         }
197 
198         if ( listener == null )
199         {
200             listener = new DefaultReleaseManagerListener();
201             if ( descriptor instanceof ContinuumReleaseDescriptor )
202             {
203                 listener.setUsername( ( (ContinuumReleaseDescriptor) descriptor ).getReleaseBy() );
204             }
205         }
206 
207         try
208         {
209             getListeners().put( releaseId, listener );
210 
211             performReleaseQueue.put( new PerformReleaseProjectTask( releaseId, descriptor, buildDirectory, goals,
212                                                                     useReleaseProfile,
213                                                                     (ReleaseManagerListener) listener, repository ) );
214         }
215         catch ( TaskQueueException e )
216         {
217             throw new ContinuumReleaseException( "Failed to add perform release task in queue.", e );
218         }
219     }
220 
221     public void rollback( String releaseId, String workingDirectory, ContinuumReleaseManagerListener listener )
222         throws ContinuumReleaseException
223     {
224         ReleaseDescriptor descriptor = readReleaseDescriptor( workingDirectory );
225 
226         if ( listener == null )
227         {
228             listener = new DefaultReleaseManagerListener();
229             if ( descriptor instanceof ContinuumReleaseDescriptor )
230             {
231                 listener.setUsername( ( (ContinuumReleaseDescriptor) descriptor ).getReleaseBy() );
232             }
233         }
234 
235         rollback( releaseId, descriptor, listener );
236     }
237 
238     private void rollback( String releaseId, ReleaseDescriptor descriptor, ContinuumReleaseManagerListener listener )
239         throws ContinuumReleaseException
240     {
241         Task releaseTask = new RollbackReleaseProjectTask( releaseId, descriptor, (ReleaseManagerListener) listener );
242 
243         try
244         {
245             rollbackReleaseQueue.put( releaseTask );
246         }
247         catch ( TaskQueueException e )
248         {
249             throw new ContinuumReleaseException( "Failed to rollback release.", e );
250         }
251     }
252 
253     public Map<String, ReleaseDescriptor> getPreparedReleases()
254     {
255         if ( preparedReleases == null )
256         {
257             preparedReleases = Collections.synchronizedMap( new LinkedHashMap<String, ReleaseDescriptor>() );
258         }
259 
260         return preparedReleases;
261     }
262 
263     public Map<String, String> getPreparedReleasesForProject( String groupId, String artifactId )
264     {
265         String key = ArtifactUtils.versionlessKey( groupId, artifactId );
266 
267         Map<String, String> projectPreparedReleases = new LinkedHashMap<String, String>();
268         Map<String, ReleaseDescriptor> preparedReleases = getPreparedReleases();
269         for ( String releaseId : preparedReleases.keySet() )
270         {
271             // get exact match, or one with a timestamp appended
272             if ( releaseId.equals( key ) || releaseId.startsWith( key + ":" ) )
273             {
274                 ReleaseDescriptor descriptor = preparedReleases.get( releaseId );
275 
276                 // use key to lookup, not release ID - versions don't get any timestamp appended
277                 projectPreparedReleases.put( releaseId, descriptor.getReleaseVersions().get( key ).toString() );
278             }
279         }
280         return projectPreparedReleases;
281     }
282 
283     public Map getReleaseResults()
284     {
285         if ( releaseResults == null )
286         {
287             releaseResults = new Hashtable();
288         }
289 
290         return releaseResults;
291     }
292 
293     private ReleaseDescriptor getReleaseDescriptor( Project project, Properties releaseProperties,
294                                                     Map<String, String> relVersions, Map<String, String> devVersions,
295                                                     Map<String, String> environments, String workingDirectory,
296                                                     String executable )
297     {
298         ContinuumReleaseDescriptor descriptor = new ContinuumReleaseDescriptor();
299 
300         //release properties from the project
301         descriptor.setWorkingDirectory( workingDirectory );
302         descriptor.setScmSourceUrl( project.getScmUrl() );
303 
304         //required properties
305         descriptor.setScmReleaseLabel( releaseProperties.getProperty( "scm-tag" ) );
306         descriptor.setScmTagBase( releaseProperties.getProperty( "scm-tagbase" ) );
307         descriptor.setReleaseVersions( relVersions );
308         descriptor.setDevelopmentVersions( devVersions );
309         descriptor.setPreparationGoals( releaseProperties.getProperty( "preparation-goals" ) );
310         descriptor.setAdditionalArguments( releaseProperties.getProperty( "arguments" ) );
311         descriptor.setAddSchema( Boolean.valueOf( releaseProperties.getProperty( "add-schema" ) ) );
312         descriptor.setAutoVersionSubmodules( Boolean.valueOf( releaseProperties.getProperty(
313             "auto-version-submodules" ) ) );
314 
315         String useEditMode = releaseProperties.getProperty( "use-edit-mode" );
316         if ( BooleanUtils.toBoolean( useEditMode ) )
317         {
318             descriptor.setScmUseEditMode( Boolean.valueOf( useEditMode ) );
319         }
320 
321         LocalRepository repository = project.getProjectGroup().getLocalRepository();
322 
323         if ( repository != null )
324         {
325             String args = descriptor.getAdditionalArguments();
326 
327             if ( StringUtils.isNotEmpty( args ) )
328             {
329                 descriptor.setAdditionalArguments( args +
330                                                        " \"-Dmaven.repo.local=" + repository.getLocation() + "\"" );
331             }
332             else
333             {
334                 descriptor.setAdditionalArguments( "\"-Dmaven.repo.local=" + repository.getLocation() + "\"" );
335             }
336         }
337 
338         //other properties
339         if ( releaseProperties.containsKey( "scm-username" ) )
340         {
341             descriptor.setScmUsername( releaseProperties.getProperty( "scm-username" ) );
342         }
343         if ( releaseProperties.containsKey( "scm-password" ) )
344         {
345             descriptor.setScmPassword( releaseProperties.getProperty( "scm-password" ) );
346         }
347         if ( releaseProperties.containsKey( "scm-comment-prefix" ) )
348         {
349             descriptor.setScmCommentPrefix( releaseProperties.getProperty( "scm-comment-prefix" ) );
350         }
351         if ( releaseProperties.containsKey( "use-release-profile" ) )
352         {
353             descriptor.setUseReleaseProfile( Boolean.valueOf( releaseProperties.getProperty(
354                 "use-release-profile" ) ) );
355         }
356 
357         //forced properties
358         descriptor.setInteractive( false );
359 
360         //set environments
361         descriptor.setEnvironments( environments );
362         descriptor.setExecutable( executable );
363 
364         //release by
365         descriptor.setReleaseBy( releaseProperties.getProperty( "release-by" ) );
366 
367         return descriptor;
368     }
369 
370     private ReleaseDescriptor readReleaseDescriptor( String workingDirectory )
371         throws ContinuumReleaseException
372     {
373         ReleaseDescriptor descriptor = new ContinuumReleaseDescriptor();
374         descriptor.setWorkingDirectory( workingDirectory );
375 
376         try
377         {
378             descriptor = releaseStore.read( descriptor );
379         }
380         catch ( ReleaseDescriptorStoreException e )
381         {
382             throw new ContinuumReleaseException( "Failed to parse descriptor file.", e );
383         }
384 
385         return descriptor;
386     }
387 
388     public Map<String, ContinuumReleaseManagerListener> getListeners()
389     {
390         if ( listeners == null )
391         {
392             listeners = new Hashtable<String, ContinuumReleaseManagerListener>();
393         }
394 
395         return listeners;
396     }
397 
398     public String sanitizeTagName( String scmUrl, String tagName )
399         throws Exception
400     {
401         ScmRepository scmRepo = scmManager.makeScmRepository( scmUrl );
402         ScmProvider scmProvider = scmManager.getProviderByRepository( scmRepo );
403         return scmProvider.sanitizeTagName( tagName );
404     }
405 
406     public ReleaseListenerSummary getListener( String releaseId )
407     {
408         ContinuumReleaseManagerListener listener = (ContinuumReleaseManagerListener) getListeners().get( releaseId );
409 
410         if ( listener != null )
411         {
412             ReleaseListenerSummary listenerSummary = new ReleaseListenerSummary();
413             listenerSummary.setGoalName( listener.getGoalName() );
414             listenerSummary.setError( listener.getError() );
415             listenerSummary.setInProgress( listener.getInProgress() );
416             listenerSummary.setState( listener.getState() );
417             listenerSummary.setPhases( listener.getPhases() );
418             listenerSummary.setCompletedPhases( listener.getCompletedPhases() );
419             listenerSummary.setUsername( listener.getUsername() );
420 
421             return listenerSummary;
422         }
423 
424         return null;
425     }
426 
427     public boolean isExecutingRelease()
428         throws Exception
429     {
430         return prepareReleaseQueue.getQueueSnapshot().size() > 0 ||
431             performReleaseQueue.getQueueSnapshot().size() > 0 ||
432             rollbackReleaseQueue.getQueueSnapshot().size() > 0 ||
433             getPerformReleaseTaskQueueExecutor().getCurrentTask() != null ||
434             getPrepareReleaseTaskQueueExecutor().getCurrentTask() != null ||
435             getRollbackReleaseTaskQueueExecutor().getCurrentTask() != null;
436     }
437 
438 
439     public TaskQueueExecutor getPerformReleaseTaskQueueExecutor()
440         throws TaskQueueManagerException
441     {
442         try
443         {
444             return (TaskQueueExecutor) container.lookup( TaskQueueExecutor.class,
445                                                          PLEXUS_KEY_PERFORM_RELEASE_TASKQUEUE_EXECUTOR );
446         }
447         catch ( ComponentLookupException e )
448         {
449             throw new TaskQueueManagerException( e.getMessage(), e );
450         }
451     }
452 
453     public TaskQueueExecutor getPrepareReleaseTaskQueueExecutor()
454         throws TaskQueueManagerException
455     {
456         try
457         {
458             return (TaskQueueExecutor) container.lookup( TaskQueueExecutor.class,
459                                                          PLEXUS_KEY_PREPARE_RELEASE_TASKQUEUE_EXECUTOR );
460         }
461         catch ( ComponentLookupException e )
462         {
463             throw new TaskQueueManagerException( e.getMessage(), e );
464         }
465     }
466 
467     public TaskQueueExecutor getRollbackReleaseTaskQueueExecutor()
468         throws TaskQueueManagerException
469     {
470         try
471         {
472             return (TaskQueueExecutor) container.lookup( TaskQueueExecutor.class,
473                                                          PLEXUS_KEY_ROLLBACK_RELEASE_TASKQUEUE_EXECUTOR );
474         }
475         catch ( ComponentLookupException e )
476         {
477             throw new TaskQueueManagerException( e.getMessage(), e );
478         }
479     }
480 
481     public void contextualize( Context context )
482         throws ContextException
483     {
484         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
485     }
486 }