View Javadoc
1   package org.apache.maven.shared.release.phase;
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.text.DateFormat;
24  import java.text.SimpleDateFormat;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Date;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.TimeZone;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.ArtifactUtils;
35  import org.apache.maven.model.Build;
36  import org.apache.maven.model.BuildBase;
37  import org.apache.maven.model.Model;
38  import org.apache.maven.model.ModelBase;
39  import org.apache.maven.model.Plugin;
40  import org.apache.maven.model.Profile;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.scm.ScmException;
43  import org.apache.maven.scm.ScmFileSet;
44  import org.apache.maven.scm.command.edit.EditScmResult;
45  import org.apache.maven.scm.manager.NoSuchScmProviderException;
46  import org.apache.maven.scm.provider.ScmProvider;
47  import org.apache.maven.scm.repository.ScmRepository;
48  import org.apache.maven.scm.repository.ScmRepositoryException;
49  import org.apache.maven.shared.release.ReleaseExecutionException;
50  import org.apache.maven.shared.release.ReleaseFailureException;
51  import org.apache.maven.shared.release.ReleaseResult;
52  import org.apache.maven.shared.release.config.ReleaseDescriptor;
53  import org.apache.maven.shared.release.env.ReleaseEnvironment;
54  import org.apache.maven.shared.release.scm.ReleaseScmCommandException;
55  import org.apache.maven.shared.release.scm.ReleaseScmRepositoryException;
56  import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
57  import org.apache.maven.shared.release.scm.ScmTranslator;
58  import org.apache.maven.shared.release.transform.ModelETLRequest;
59  import org.apache.maven.shared.release.transform.MavenCoordinate;
60  import org.apache.maven.shared.release.transform.ModelETL;
61  import org.apache.maven.shared.release.transform.ModelETLFactory;
62  import org.apache.maven.shared.release.transform.jdom2.JDomModelETLFactory;
63  import org.apache.maven.shared.release.util.ReleaseUtil;
64  import org.codehaus.plexus.component.annotations.Requirement;
65  import org.codehaus.plexus.util.StringUtils;
66  
67  /**
68   * Base class for rewriting phases.
69   *
70   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
71   */
72  public abstract class AbstractRewritePomsPhase
73      extends AbstractReleasePhase implements ResourceGenerator
74  {
75      /**
76       * Tool that gets a configured SCM repository from release configuration.
77       */
78      @Requirement
79      private ScmRepositoryConfigurator scmRepositoryConfigurator;
80  
81      @Requirement( role = ModelETLFactory.class )
82      private Map<String, ModelETLFactory> modelETLFactories;
83  
84      /**
85       * Use jdom2-sax as default
86       */
87      private String modelETL = JDomModelETLFactory.ROLE_HINT;
88  
89      /**
90       * SCM URL translators mapped by provider name.
91       */
92      @Requirement( role = ScmTranslator.class )
93      private Map<String, ScmTranslator> scmTranslators;
94  
95      /**
96       * <p>Getter for the field <code>scmTranslators</code>.</p>
97       *
98       * @return a {@link java.util.Map} object
99       */
100     protected final Map<String, ScmTranslator> getScmTranslators()
101     {
102         return scmTranslators;
103     }
104 
105     private String ls = ReleaseUtil.LS;
106 
107     /**
108      * <p>Setter for the field <code>ls</code>.</p>
109      *
110      * @param ls a {@link java.lang.String} object
111      */
112     public void setLs( String ls )
113     {
114         this.ls = ls;
115     }
116 
117     /**
118      * <p>Setter for the field <code>modelETL</code>.</p>
119      *
120      * @param modelETL a {@link java.lang.String} object
121      */
122     public void setModelETL( String modelETL )
123     {
124         this.modelETL = modelETL;
125     }
126 
127     private long startTime = -1 * 1000;
128 
129     /**
130      * <p>Setter for the field <code>startTime</code>.</p>
131      *
132      * @param startTime a long
133      */
134     public void setStartTime( long startTime )
135     {
136         this.startTime = startTime;
137     }
138 
139     /**
140      * <p>getPomSuffix.</p>
141      *
142      * @return a {@link java.lang.String} object
143      */
144     protected abstract String getPomSuffix();
145 
146     @Override
147     public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
148                                   List<MavenProject> reactorProjects )
149         throws ReleaseExecutionException, ReleaseFailureException
150     {
151         ReleaseResult result = new ReleaseResult();
152 
153         transform( releaseDescriptor, releaseEnvironment, reactorProjects, false, result );
154 
155         result.setResultCode( ReleaseResult.SUCCESS );
156 
157         return result;
158     }
159 
160     @Override
161     public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
162                                    List<MavenProject> reactorProjects )
163         throws ReleaseExecutionException, ReleaseFailureException
164     {
165         ReleaseResult result = new ReleaseResult();
166 
167         transform( releaseDescriptor, releaseEnvironment, reactorProjects, true, result );
168 
169         result.setResultCode( ReleaseResult.SUCCESS );
170 
171         return result;
172     }
173 
174     @Override
175     public ReleaseResult clean( List<MavenProject> reactorProjects )
176     {
177         ReleaseResult result = new ReleaseResult();
178 
179         if ( reactorProjects != null )
180         {
181             for ( MavenProject project : reactorProjects )
182             {
183                 File pomFile = ReleaseUtil.getStandardPom( project );
184                 // MRELEASE-273 : if no pom
185                 if ( pomFile != null )
186                 {
187                     File file = new File( pomFile.getParentFile(), pomFile.getName() + "." + getPomSuffix() );
188                     if ( file.exists() )
189                     {
190                         file.delete();
191                     }
192                 }
193             }
194         }
195 
196         result.setResultCode( ReleaseResult.SUCCESS );
197 
198         return result;
199     }
200 
201     private void transform( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
202                             List<MavenProject> reactorProjects, boolean simulate, ReleaseResult result )
203         throws ReleaseExecutionException, ReleaseFailureException
204     {
205         result.setStartTime( ( startTime >= 0 ) ? startTime : System.currentTimeMillis() );
206 
207         for ( MavenProject project : reactorProjects )
208         {
209             logInfo( result, "Transforming '" + project.getName() + "'..." );
210 
211             transformProject( project, releaseDescriptor, releaseEnvironment, simulate, result );
212         }
213     }
214 
215     private void transformProject( MavenProject project, ReleaseDescriptor releaseDescriptor,
216                                    ReleaseEnvironment releaseEnvironment, boolean simulate,
217                                    ReleaseResult result )
218         throws ReleaseExecutionException, ReleaseFailureException
219     {
220         File pomFile = ReleaseUtil.getStandardPom( project );
221 
222         ModelETLRequest request = new ModelETLRequest();
223         request.setLineSeparator( ls );
224         request.setProject( project );
225         request.setReleaseDescriptor( releaseDescriptor );
226 
227         ModelETL etl = modelETLFactories.get( modelETL ).newInstance( request );
228 
229         etl.extract( pomFile );
230 
231         ScmRepository scmRepository = null;
232         ScmProvider provider = null;
233 
234         if ( isUpdateScm() )
235         {
236             try
237             {
238                 scmRepository = scmRepositoryConfigurator.getConfiguredRepository( releaseDescriptor,
239                                                                                    releaseEnvironment.getSettings() );
240 
241                 provider = scmRepositoryConfigurator.getRepositoryProvider( scmRepository );
242             }
243             catch ( ScmRepositoryException e )
244             {
245                 throw new ReleaseScmRepositoryException( e.getMessage(), e.getValidationMessages() );
246             }
247             catch ( NoSuchScmProviderException e )
248             {
249                 throw new ReleaseExecutionException( "Unable to configure SCM repository: " + e.getMessage(), e );
250             }
251         }
252 
253         transformDocument( project, etl.getModel(), releaseDescriptor, scmRepository, result,
254                            simulate );
255 
256         File outputFile;
257         if ( simulate )
258         {
259             outputFile = new File( pomFile.getParentFile(), pomFile.getName() + "." + getPomSuffix() );
260         }
261         else
262         {
263             outputFile = pomFile;
264             prepareScm( pomFile, releaseDescriptor, scmRepository, provider );
265         }
266         etl.load( outputFile );
267 
268     }
269 
270     private void transformDocument( MavenProject project, Model modelTarget, ReleaseDescriptor releaseDescriptor,
271                                     ScmRepository scmRepository, ReleaseResult result,
272                                     boolean simulate )
273         throws ReleaseExecutionException, ReleaseFailureException
274     {
275         Model model = project.getModel();
276 
277         Properties properties = modelTarget.getProperties();
278 
279         String parentVersion = rewriteParent( project, modelTarget, releaseDescriptor, simulate );
280 
281         String projectId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
282 
283         rewriteVersion( modelTarget, releaseDescriptor, projectId, project );
284 
285         Build buildTarget = modelTarget.getBuild();
286         if ( buildTarget != null )
287         {
288             // profile.build.extensions doesn't exist, so only rewrite project.build.extensions
289             rewriteArtifactVersions( toMavenCoordinates( buildTarget.getExtensions() ), 
290                                      model, properties, result, releaseDescriptor, simulate );
291 
292             rewriteArtifactVersions( toMavenCoordinates( buildTarget.getPlugins() ), 
293                                      model, properties, result, releaseDescriptor, simulate );
294 
295             for ( Plugin plugin : buildTarget.getPlugins() )
296             {
297                 rewriteArtifactVersions( toMavenCoordinates( plugin.getDependencies() ),
298                                          model, properties,
299                                          result, releaseDescriptor, simulate );
300             }
301 
302             if ( buildTarget.getPluginManagement() != null )
303             {
304                 rewriteArtifactVersions( toMavenCoordinates( buildTarget.getPluginManagement().getPlugins() ), model,
305                                          properties, result, releaseDescriptor, simulate );
306 
307                 for ( Plugin plugin : buildTarget.getPluginManagement().getPlugins() )
308                 {
309                     rewriteArtifactVersions( toMavenCoordinates( plugin.getDependencies() ), model, properties, result,
310                                              releaseDescriptor, simulate );
311                 }
312             }
313         }
314 
315         for ( Profile profile : modelTarget.getProfiles() )
316         {
317             BuildBase profileBuild = profile.getBuild();
318             if ( profileBuild != null )
319             {
320                 rewriteArtifactVersions( toMavenCoordinates( profileBuild.getPlugins() ), model, properties, result,
321                                          releaseDescriptor, simulate );
322 
323                 for ( Plugin plugin : profileBuild.getPlugins() )
324                 {
325                     rewriteArtifactVersions( toMavenCoordinates( plugin.getDependencies() ), model, properties, result,
326                                              releaseDescriptor, simulate );
327                 }
328 
329                 if ( profileBuild.getPluginManagement() != null )
330                 {
331                     rewriteArtifactVersions( toMavenCoordinates( profileBuild.getPluginManagement().getPlugins() ),
332                                              model, properties, result, releaseDescriptor, simulate );
333 
334                     for ( Plugin plugin : profileBuild.getPluginManagement().getPlugins() )
335                     {
336                         rewriteArtifactVersions( toMavenCoordinates( plugin.getDependencies() ), model, properties,
337                                                  result, releaseDescriptor, simulate );
338                     }
339                 }
340             }
341         }
342 
343         List<ModelBase> modelBases = new ArrayList<>();
344         modelBases.add( modelTarget );
345         modelBases.addAll( modelTarget.getProfiles() );
346 
347         for ( ModelBase modelBase : modelBases )
348         {
349             rewriteArtifactVersions( toMavenCoordinates( modelBase.getDependencies() ), model, properties, result,
350                                      releaseDescriptor, simulate );
351 
352             if ( modelBase.getDependencyManagement() != null )
353             {
354                 rewriteArtifactVersions( toMavenCoordinates( modelBase.getDependencyManagement().getDependencies() ),
355                                          model, properties, result, releaseDescriptor, simulate );
356             }
357 
358             if ( modelBase.getReporting() != null )
359             {
360                 rewriteArtifactVersions( toMavenCoordinates( modelBase.getReporting().getPlugins() ), model, properties,
361                                          result, releaseDescriptor, simulate );
362             }
363         }
364 
365         transformScm( project, modelTarget, releaseDescriptor, projectId, scmRepository, result );
366 
367         if ( properties != null )
368         {
369             rewriteBuildOutputTimestampProperty( properties, result );
370         }
371     }
372 
373     private void rewriteBuildOutputTimestampProperty( Properties properties, ReleaseResult result )
374     {
375         String buildOutputTimestamp = properties.getProperty( "project.build.outputTimestamp" );
376         if ( buildOutputTimestamp == null || StringUtils.isEmpty( buildOutputTimestamp ) )
377         {
378             // no Reproducible Builds output timestamp defined
379             return;
380         }
381 
382         if ( StringUtils.isNumeric( buildOutputTimestamp ) )
383         {
384             // int representing seconds since the epoch, like SOURCE_DATE_EPOCH
385             buildOutputTimestamp = String.valueOf( result.getStartTime() / 1000 );
386         }
387         else if ( buildOutputTimestamp.length() <= 1 )
388         {
389             // value length == 1 means disable Reproducible Builds
390             return;
391         }
392         else
393         {
394             // ISO-8601
395             DateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss'Z'" );
396             df.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
397             buildOutputTimestamp = df.format( new Date( result.getStartTime() ) );
398         }
399         properties.setProperty( "project.build.outputTimestamp", buildOutputTimestamp );
400     }
401 
402     private void rewriteVersion( Model modelTarget, ReleaseDescriptor releaseDescriptor, String projectId,
403                                  MavenProject project )
404         throws ReleaseFailureException
405     {
406         String version = getNextVersion( releaseDescriptor, projectId );
407         if ( version == null )
408         {
409             throw new ReleaseFailureException( "Version for '" + project.getName() + "' was not mapped" );
410         }
411 
412         modelTarget.setVersion( version );
413     }
414 
415     private String rewriteParent( MavenProject project, Model targetModel, 
416                                   ReleaseDescriptor releaseDescriptor, boolean simulate )
417         throws ReleaseFailureException
418     {
419         String parentVersion = null;
420         if ( project.hasParent() )
421         {
422             MavenProject parent = project.getParent();
423             String key = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
424             parentVersion = getNextVersion( releaseDescriptor, key );
425             if ( parentVersion == null )
426             {
427                 //MRELEASE-317
428                 parentVersion = getResolvedSnapshotVersion( key, releaseDescriptor );
429             }
430             if ( parentVersion == null )
431             {
432                 String original = getOriginalVersion( releaseDescriptor, key, simulate );
433                 if ( parent.getVersion().equals( original ) )
434                 {
435                     throw new ReleaseFailureException( "Version for parent '" + parent.getName() + "' was not mapped" );
436                 }
437             }
438             else
439             {
440                 targetModel.getParent().setVersion( parentVersion );
441             }
442         }
443         return parentVersion;
444     }
445 
446     private void rewriteArtifactVersions( Collection<MavenCoordinate> elements, Model projectModel,
447                                           Properties properties, ReleaseResult result,
448                                           ReleaseDescriptor releaseDescriptor, boolean simulate )
449         throws ReleaseExecutionException, ReleaseFailureException
450     {
451         if ( elements == null )
452         {
453             return;
454         }
455         String projectId = ArtifactUtils.versionlessKey( projectModel.getGroupId(), projectModel.getArtifactId() );
456         for ( MavenCoordinate coordinate : elements )
457         {
458             String rawVersion = coordinate.getVersion();
459             if ( rawVersion == null )
460             {
461                 // managed dependency or unversioned plugin
462                 continue;
463             }
464 
465             String rawGroupId = coordinate.getGroupId();
466             if ( rawGroupId == null )
467             {
468                 if ( "plugin".equals( coordinate.getName() ) )
469                 {
470                     rawGroupId = "org.apache.maven.plugins";
471                 }
472                 else
473                 {
474                     // incomplete dependency
475                     continue;
476                 }
477             }
478             String groupId = ReleaseUtil.interpolate( rawGroupId, projectModel );
479 
480             String rawArtifactId = coordinate.getArtifactId();
481             if ( rawArtifactId == null )
482             {
483                 // incomplete element
484                 continue;
485             }
486             String artifactId = ReleaseUtil.interpolate( rawArtifactId, projectModel );
487 
488             String key = ArtifactUtils.versionlessKey( groupId, artifactId );
489             String resolvedSnapshotVersion = getResolvedSnapshotVersion( key, releaseDescriptor );
490             String mappedVersion = getNextVersion( releaseDescriptor, key );
491             String originalVersion = getOriginalVersion( releaseDescriptor, key, simulate );
492             if ( originalVersion == null )
493             {
494                 originalVersion = getOriginalResolvedSnapshotVersion( key, releaseDescriptor );
495             }
496 
497             // MRELEASE-220
498             if ( mappedVersion != null && mappedVersion.endsWith( Artifact.SNAPSHOT_VERSION )
499                 && !rawVersion.endsWith( Artifact.SNAPSHOT_VERSION ) && !releaseDescriptor.isUpdateDependencies() )
500             {
501                 continue;
502             }
503 
504             if ( mappedVersion != null )
505             {
506                 if ( rawVersion.equals( originalVersion ) )
507                 {
508                     logInfo( result, "  Updating " + artifactId + " to " + mappedVersion );
509                     coordinate.setVersion( mappedVersion );
510                 }
511                 else if ( rawVersion.matches( "\\$\\{.+\\}" ) )
512                 {
513                     String expression = rawVersion.substring( 2, rawVersion.length() - 1 );
514 
515                     if ( expression.startsWith( "project." ) || expression.startsWith( "pom." )
516                         || "version".equals( expression ) )
517                     {
518                         if ( !mappedVersion.equals( getNextVersion( releaseDescriptor, projectId ) ) )
519                         {
520                             logInfo( result, "  Updating " + artifactId + " to " + mappedVersion );
521                             coordinate.setVersion( mappedVersion );
522                         }
523                         else
524                         {
525                             logInfo( result, "  Ignoring artifact version update for expression " + rawVersion );
526                         }
527                     }
528                     else if ( properties != null )
529                     {
530                         // version is an expression, check for properties to update instead
531 
532                         String propertyValue = properties.getProperty( expression );
533 
534                         if ( propertyValue != null )
535                         {
536                             if ( propertyValue.equals( originalVersion ) )
537                             {
538                                 logInfo( result, "  Updating " + rawVersion + " to " + mappedVersion );
539                                 // change the property only if the property is the same as what's in the reactor
540                                 properties.setProperty( expression, mappedVersion );
541                             }
542                             else if ( mappedVersion.equals( propertyValue ) )
543                             {
544                                 // this property may have been updated during processing a sibling.
545                                 logInfo( result, "  Ignoring artifact version update for expression " + rawVersion
546                                     + " because it is already updated" );
547                             }
548                             else if ( !mappedVersion.equals( rawVersion ) )
549                             {
550                                 if ( mappedVersion.matches( "\\$\\{project.+\\}" )
551                                     || mappedVersion.matches( "\\$\\{pom.+\\}" )
552                                     || "${version}".equals( mappedVersion ) )
553                                 {
554                                     logInfo( result, "  Ignoring artifact version update for expression "
555                                         + mappedVersion );
556                                     // ignore... we cannot update this expression
557                                 }
558                                 else
559                                 {
560                                     // the value of the expression conflicts with what the user wanted to release
561                                     throw new ReleaseFailureException( "The artifact (" + key + ") requires a "
562                                         + "different version (" + mappedVersion + ") than what is found ("
563                                         + propertyValue + ") for the expression (" + expression + ") in the "
564                                         + "project (" + projectId + ")." );
565                                 }
566                             }
567                         }
568                         else
569                         {
570                             // the expression used to define the version of this artifact may be inherited
571                             // TODO needs a better error message, what pom? what dependency?
572                             throw new ReleaseFailureException( "The version could not be updated: " + rawVersion );
573                         }
574                     }
575                 }
576                 else
577                 {
578                     // different/previous version not related to current release
579                 }
580             }
581             else if ( resolvedSnapshotVersion != null )
582             {
583                 logInfo( result, "  Updating " + artifactId + " to " + resolvedSnapshotVersion );
584 
585                 coordinate.setVersion( resolvedSnapshotVersion );
586             }
587             else
588             {
589                 // artifact not related to current release
590             }
591         }
592     }
593 
594     private void prepareScm( File pomFile, ReleaseDescriptor releaseDescriptor, ScmRepository repository,
595                            ScmProvider provider )
596         throws ReleaseExecutionException, ReleaseScmCommandException
597     {
598         try
599         {
600             if ( isUpdateScm() && ( releaseDescriptor.isScmUseEditMode() || provider.requiresEditMode() ) )
601             {
602                 EditScmResult result = provider.edit( repository, new ScmFileSet(
603                     new File( releaseDescriptor.getWorkingDirectory() ), pomFile ) );
604 
605                 if ( !result.isSuccess() )
606                 {
607                     throw new ReleaseScmCommandException( "Unable to enable editing on the POM", result );
608                 }
609             }
610         }
611         catch ( ScmException e )
612         {
613             throw new ReleaseExecutionException( "An error occurred enabling edit mode: " + e.getMessage(), e );
614         }
615     }
616 
617 
618     /**
619      * <p>getResolvedSnapshotVersion.</p>
620      *
621      * @param artifactVersionlessKey a {@link java.lang.String} object
622      * @param releaseDscriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
623      * @return a {@link java.lang.String} object
624      */
625     protected abstract String getResolvedSnapshotVersion( String artifactVersionlessKey,
626                                                           ReleaseDescriptor releaseDscriptor );
627 
628     /**
629      * <p>getOriginalVersion.</p>
630      *
631      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
632      * @param projectKey a {@link java.lang.String} object
633      * @param simulate a boolean
634      * @return a {@link java.lang.String} object
635      */
636     protected abstract String getOriginalVersion( ReleaseDescriptor releaseDescriptor, String projectKey,
637                                                   boolean simulate );
638 
639     /**
640      * <p>getNextVersion.</p>
641      *
642      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
643      * @param key a {@link java.lang.String} object
644      * @return a {@link java.lang.String} object
645      */
646     protected abstract String getNextVersion( ReleaseDescriptor releaseDescriptor, String key );
647 
648     /**
649      * <p>transformScm.</p>
650      *
651      * @param project a {@link org.apache.maven.project.MavenProject} object
652      * @param modelTarget a {@link org.apache.maven.model.Model} object
653      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
654      * @param projectId a {@link java.lang.String} object
655      * @param scmRepository a {@link org.apache.maven.scm.repository.ScmRepository} object
656      * @param result a {@link org.apache.maven.shared.release.ReleaseResult} object
657      * @throws org.apache.maven.shared.release.ReleaseExecutionException if any.
658      */
659     protected abstract void transformScm( MavenProject project, Model modelTarget, ReleaseDescriptor releaseDescriptor,
660                                           String projectId, ScmRepository scmRepository,
661                                           ReleaseResult result )
662         throws ReleaseExecutionException;
663 
664     /**
665      * <p>isUpdateScm.</p>
666      *
667      * @return {@code true} if the SCM-section should be updated, otherwise {@code false}
668      * @since 2.4
669      */
670     protected boolean isUpdateScm()
671     {
672         return true;
673     }
674 
675     /**
676      * <p>getOriginalResolvedSnapshotVersion.</p>
677      *
678      * @param artifactVersionlessKey a {@link java.lang.String} object
679      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
680      * @return a {@link java.lang.String} object
681      */
682     protected String getOriginalResolvedSnapshotVersion( String artifactVersionlessKey,
683                                                          ReleaseDescriptor releaseDescriptor )
684     {
685         return releaseDescriptor.getDependencyOriginalVersion( artifactVersionlessKey );
686     }
687 
688     /**
689      * Determines the relative path from trunk to tag, and adds this relative path
690      * to the url.
691      *
692      * @param trunkPath - The trunk url
693      * @param tagPath   - The tag base
694      * @param urlPath   - scm.url or scm.connection
695      * @return The url path for the tag.
696      */
697     protected static String translateUrlPath( String trunkPath, String tagPath, String urlPath )
698     {
699         trunkPath = trunkPath.trim();
700         tagPath = tagPath.trim();
701         //Strip the slash at the end if one is present
702         if ( trunkPath.endsWith( "/" ) )
703         {
704             trunkPath = trunkPath.substring( 0, trunkPath.length() - 1 );
705         }
706         if ( tagPath.endsWith( "/" ) )
707         {
708             tagPath = tagPath.substring( 0, tagPath.length() - 1 );
709         }
710         char[] tagPathChars = trunkPath.toCharArray();
711         char[] trunkPathChars = tagPath.toCharArray();
712         // Find the common path between trunk and tags
713         int i = 0;
714         while ( ( i < tagPathChars.length ) && ( i < trunkPathChars.length ) && tagPathChars[i] == trunkPathChars[i] )
715         {
716             ++i;
717         }
718         // If there is nothing common between trunk and tags, or the relative
719         // path does not exist in the url, then just return the tag.
720         if ( i == 0 || urlPath.indexOf( trunkPath.substring( i ) ) < 0 )
721         {
722             return tagPath;
723         }
724         else
725         {
726             return StringUtils.replace( urlPath, trunkPath.substring( i ), tagPath.substring( i ) );
727         }
728     }
729 
730     private Collection<MavenCoordinate> toMavenCoordinates( List<?> objects )
731     {
732         Collection<MavenCoordinate> coordinates = new ArrayList<>( objects.size() );
733         for ( Object object : objects )
734         {
735             if ( object instanceof MavenCoordinate )
736             {
737                 coordinates.add( (MavenCoordinate) object );
738             }
739             else
740             {
741                 throw new UnsupportedOperationException();
742             }
743         }
744         return coordinates;
745     }
746 
747 
748 }