View Javadoc
1   package org.apache.maven.plugin.cxx;
2   
3   /*
4    * Copyright (C) 2011-2016, Neticoa SAS France - Tous droits réservés.
5    * Author(s) : Franck Bonin, Neticoa SAS France
6    *
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   *
19   */
20   
21  import org.apache.maven.artifact.Artifact;
22  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
23  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
24  import org.apache.maven.plugin.MojoExecutionException;
25  import org.apache.maven.plugin.dependency.utils.DependencyStatusSets;
26  import org.apache.maven.plugin.dependency.utils.DependencyUtil;
27  import org.apache.maven.plugin.dependency.utils.filters.MarkerFileFilter;
28  import org.apache.maven.plugin.dependency.utils.markers.DefaultFileMarkerHandler;
29  import org.apache.maven.plugins.annotations.LifecyclePhase;
30  import org.apache.maven.plugins.annotations.Mojo;
31  import org.apache.maven.plugins.annotations.Parameter;
32  import org.apache.maven.plugins.annotations.Component;
33  import org.apache.maven.plugins.annotations.ResolutionScope;
34  import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
35  
36  import org.codehaus.plexus.util.StringUtils;
37  
38  import java.io.File;
39  import java.io.IOException;
40  import java.util.Set;
41  import java.util.ArrayList;
42  import java.util.regex.Pattern;
43  import java.util.Properties;
44  
45  import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
46  import org.apache.maven.plugin.cxx.utils.ClassifierRegexFilter;
47  import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
48  import org.apache.maven.shared.artifact.filter.collection.ClassifierFilter;
49  import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
50  import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter;
51  import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter;
52  import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
53  import org.apache.maven.shared.artifact.filter.collection.TypeFilter;  
54  
55  import org.apache.maven.project.MavenProject;
56  import org.apache.maven.project.MavenProjectBuilder;
57  import org.apache.maven.project.ProjectBuildingException;
58  import org.apache.maven.model.Scm;
59  
60  import org.apache.maven.plugin.cxx.utils.SourceTarget;
61  import org.apache.maven.plugin.cxx.utils.Credential;
62  import org.apache.maven.plugin.cxx.utils.svn.SvnService;
63  import org.apache.maven.plugin.cxx.utils.svn.SvnInfo;
64  import org.apache.maven.plugin.cxx.utils.svn.SvnExternalEntry;
65  import org.apache.maven.plugin.cxx.utils.svn.SvnExternalsEntries;
66  
67  import org.apache.maven.settings.Settings;
68  // require Maven 3 API :
69  /*import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
70  import org.apache.maven.settings.crypto.SettingsDecrypter;
71  import org.apache.maven.settings.crypto.SettingsDecryptionRequest;
72  import org.apache.maven.settings.crypto.SettingsDecryptionResult;*/
73  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
74  
75  /**
76   * Goal that retrieve source dependencies from the SCM.
77   *
78   * @author Franck Bonin
79   * @since 0.0.6
80   */
81  @Mojo( name = "scm-dependencies", requiresDependencyResolution = ResolutionScope.TEST,
82         defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
83  public class ScmDependenciesMojo
84          extends org.apache.maven.plugin.dependency.fromDependencies.AbstractDependencyFilterMojo
85  {
86      /**
87       * Pom directory location
88       * 
89       * @since 0.0.6
90       */
91      @Parameter( property = "basedir", readonly = true, required = true )
92      protected File basedir;
93      
94      /**
95       * Optional source dependencies sub-directory holder
96       * 
97       * @since 0.0.6
98       */
99      @Parameter( property = "sourceSubdir", defaultValue = "." )
100     protected String sourceSubdir;
101     
102     /**
103      * svn:externals shall use precise svn revisions retrieved from scm info at execution time
104      * 
105      * @since 0.0.6
106      */
107     @Parameter( property = "sourceFreezeRevision", defaultValue = "false" )    
108     protected boolean sourceFreezeRevision;
109     
110     // @formatter:off
111     /**
112      * List of String prefix to remove when creating dependencies target dirs
113      * 
114      * Example :
115      *<pre>{@code
116      *<sourceTargetDirRemovePrefixes>
117      *  <sourceRemoveTargetPrefixe>fr/neticoa</sourceRemoveTargetPrefixe>
118      *  <sourceRemoveTargetPrefixe>module</sourceRemoveTargetPrefixe>
119      *</sourceTargetDirRemovePrefixes>}</pre>
120      * @since 0.0.6
121      */
122     // @formatter:on
123     @Parameter( )       
124     protected String[] sourceTargetDirRemovePrefixes = null;
125     
126     // @formatter:off
127     /**
128      * Provide explicit target path for each source dependency
129      * 
130      * Exemple :
131      *<pre>{@code
132      *<sourceTargets>
133      *  <sourceTarget>
134      *    <dependency>
135      *      <groupId>fr.neticoa</groupId>
136      *      <artifactId>module</artifactId>
137      *    </dependency>
138      *    <targetDir>src/module</targetDir>
139      *  </sourceTarget>
140      *</sourceTargets>}</pre>
141      * @since 0.0.6
142      */
143     // @formatter:on
144     @Parameter( )
145     private SourceTarget[] sourceTargets = null;
146     
147     /**
148      * Comma Separated list of Classifiers to include. Empty String indicates
149      * include everything (default).
150      *
151      * @since 0.0.6
152      */
153     @Parameter( property = "includeRegexClassifiers", defaultValue = "" )
154     protected String includeRegexClassifiers;
155 
156     /**
157      * Comma Separated list of Classifiers to exclude. Empty String indicates
158      * don't exclude anything (default).
159      *
160      * @since 0.0.6
161      */
162     @Parameter( property = "excludeRegexClassifiers", defaultValue = "" )
163     protected String excludeRegexClassifiers;
164     
165     /**
166      * SCM connection information to use
167      * 
168      * @since 0.0.6
169      */
170     @Parameter( property = "connectionType", defaultValue = "connection" )
171     protected String connectionType;
172     
173     /**
174      * The user name (used by svn).
175      * 
176      * You may use maven setting to store username. 
177      * See http://maven.apache.org/guides/mini/guide-encryption.html
178      *
179      * @since 0.0.6
180      */
181     @Parameter( property = "username" )
182     private String username = null;
183 
184     /**
185      * The user password (used by svn).
186      * 
187      * You may use maven setting to store encrypted password. 
188      * See http://maven.apache.org/guides/mini/guide-encryption.html
189      *
190      * @since 0.0.6
191      */
192     @Parameter( property = "password" )
193     private String password = null;
194     
195     /**
196      * The server id to use in maven settings to retrieve credential.
197      * Optionnal, by defaut each scm url "hostname[:port]" is taken as server id to search potential
198      * credentials in maven settings
199      * 
200      * See http://maven.apache.org/guides/mini/guide-encryption.html
201      *
202      * @since 0.0.6
203      */
204     @Parameter( property = "settingsServerId" )
205     private String settingsServerId = null;
206     
207     /**
208      * Maven settings.
209      *
210      * @since 0.0.6
211      */
212     @Parameter( defaultValue = "${settings}", readonly = true )
213     private Settings settings;
214     //@Component
215     //private Settings settings;
216         
217     /**
218      * The decrypter for passwords.
219      * 
220      * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be
221      * used instead.
222      */
223     @Component( hint = "mng-4384" )
224     private SecDispatcher secDispatcher;
225     //@Component
226     //private SettingsDecrypter settingsDecrypter;
227     
228     /**
229      * origin : derived from org.apache.maven.plugin.dependency.fromDependencies.UnpackDependenciesMojo
230      * $FB duplicate with UnPackDependenciesMojo
231      */
232     protected ArtifactsFilter getMarkedArtifactFilter()
233     {
234         return new MarkerFileFilter( this.overWriteReleases, this.overWriteSnapshots, this.overWriteIfNewer,
235                                      new DefaultFileMarkerHandler( this.markersDirectory ) );
236     }
237   
238     /**
239      * origin : org.apache.maven.plugin.dependency.fromDependencies.AbstractDependencyFilterMojo
240      * $FB duplicate with UnPackDependenciesMojo
241      */
242     @Component
243     MavenProjectBuilder myProjectBuilder;
244     
245     /**
246      * origin : org.apache.maven.plugin.dependency.fromDependencies.AbstractDependencyFilterMojo
247      * $FB duplicate with UnPackDependenciesMojo
248      */
249     private MavenProject buildProjectFromArtifact( Artifact artifact )
250         throws MojoExecutionException
251     {
252         try
253         {
254             return myProjectBuilder.buildFromRepository( artifact, remoteRepos, getLocal() );
255         }
256         catch ( ProjectBuildingException e )
257         {
258             throw new MojoExecutionException( e.getMessage(), e );
259         }
260     }
261   
262     /**
263      * origin : org.apache.maven.plugin.dependency.fromDependencies.AbstractDependencyFilterMojo
264      * $FB duplicate with UnPackDependenciesMojo
265      */
266     private void addParentArtifacts( MavenProject project, Set<Artifact> artifacts )
267         throws MojoExecutionException
268     {
269         while ( project.hasParent() )
270         {
271             project = project.getParent();
272 
273             if ( project.getArtifact() == null )
274             {
275                 // Maven 2.x bug
276                 Artifact artifact =
277                     factory.createBuildArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(),
278                                                  project.getPackaging() );
279                 project.setArtifact( artifact );
280             }
281 
282             if ( !artifacts.add( project.getArtifact() ) )
283             {
284                 // artifact already in the set
285                 break;
286             }
287             try
288             {
289                 resolver.resolve( project.getArtifact(), this.remoteRepos, this.getLocal() );
290             }
291             catch ( ArtifactResolutionException e )
292             {
293                 throw new MojoExecutionException( e.getMessage(), e );
294             }
295             catch ( ArtifactNotFoundException e )
296             {
297                 throw new MojoExecutionException( e.getMessage(), e );
298             }
299         }
300     }
301     
302     /**
303      * Method creates filters and filters the projects dependencies. This method
304      * also transforms the dependencies if classifier is set. The dependencies
305      * are filtered in least specific to most specific order
306      * 
307      * origin : derived from org.apache.maven.plugin.dependency.fromDependencies.AbstractDependencyFilterMojo
308      *
309      * @param stopOnFailure
310      * @return DependencyStatusSets - Bean of TreeSets that contains information
311      *         on the projects dependencies
312      * @throws MojoExecutionException
313      */
314     protected DependencyStatusSets getDependencySets( boolean stopOnFailure, boolean includeParents )
315         throws MojoExecutionException
316     {
317         // add filters in well known order, least specific to most specific
318         FilterArtifacts filter = new FilterArtifacts();
319 
320         filter.addFilter( new ProjectTransitivityFilter( project.getDependencyArtifacts(), this.excludeTransitive ) );
321 
322         filter.addFilter( new ScopeFilter( DependencyUtil.cleanToBeTokenizedString( this.includeScope ),
323                                            DependencyUtil.cleanToBeTokenizedString( this.excludeScope ) ) );
324 
325         /*filter.addFilter( new TypeFilter( DependencyUtil.cleanToBeTokenizedString( this.includeTypes ),
326                                           DependencyUtil.cleanToBeTokenizedString( this.excludeTypes ) ) );*/
327         filter.addFilter( new TypeFilter( DependencyUtil.cleanToBeTokenizedString( "pom" ),
328                                           DependencyUtil.cleanToBeTokenizedString( null ) ) );
329 
330         filter.addFilter( new ClassifierFilter( DependencyUtil.cleanToBeTokenizedString( this.includeClassifiers ),
331                                                 DependencyUtil.cleanToBeTokenizedString( this.excludeClassifiers ) ) );
332                                                 
333         filter.addFilter( new ClassifierRegexFilter (
334             DependencyUtil.cleanToBeTokenizedString( this.includeRegexClassifiers ),
335             DependencyUtil.cleanToBeTokenizedString( this.excludeRegexClassifiers ) ) );  
336 
337         filter.addFilter( new GroupIdFilter( DependencyUtil.cleanToBeTokenizedString( this.includeGroupIds ),
338                                              DependencyUtil.cleanToBeTokenizedString( this.excludeGroupIds ) ) );
339 
340         filter.addFilter( new ArtifactIdFilter( DependencyUtil.cleanToBeTokenizedString( this.includeArtifactIds ),
341                                                 DependencyUtil.cleanToBeTokenizedString( this.excludeArtifactIds ) ) );
342                                                                                
343 
344         // start with all artifacts.
345         @SuppressWarnings( "unchecked" ) Set<Artifact> artifacts = project.getArtifacts();
346 
347         if ( includeParents )
348         {
349             // add dependencies parents
350             for ( Artifact dep : new ArrayList<Artifact>( artifacts ) )
351             {
352                 addParentArtifacts( buildProjectFromArtifact( dep ), artifacts );
353             }
354 
355             // add current project parent
356             addParentArtifacts( project, artifacts );
357         }
358 
359         // perform filtering
360         try
361         {
362             artifacts = filter.filter( artifacts );
363         }
364         catch ( ArtifactFilterException e )
365         {
366             throw new MojoExecutionException( e.getMessage(), e );
367         }
368 
369         // transform artifacts if classifier is set
370         DependencyStatusSets status;
371         if ( StringUtils.isNotEmpty( classifier ) )
372         {
373             status = getClassifierTranslatedDependencies( artifacts, stopOnFailure );
374         }
375         else
376         {
377             status = filterMarkedDependencies( artifacts );
378         }
379 
380         return status;
381     }
382 
383     /**
384      * Returns the list of servers with decrypted passwords.
385      *
386      * @return list of servers with decrypted passwords.
387      */
388     /*List<Server> getDecryptedServers()
389     {
390         final SettingsDecryptionRequest settingsDecryptionRequest = new DefaultSettingsDecryptionRequest();
391         settingsDecryptionRequest.setServers( settings.getServers() );
392         final SettingsDecryptionResult decrypt = settingsDecrypter.decrypt( settingsDecryptionRequest );
393         return decrypt.getServers();
394     }*/
395 
396     SvnExternalEntry buildExternalEntryFromProvidedInfos( String targetDir, Artifact artifact,
397         MavenProject dependencyProject, SvnInfo dependencySvnInfo, SvnInfo rootSvnInfo )
398     {
399         if ( null != sourceTargetDirRemovePrefixes )
400         {
401             for ( String sourceTargetDirRemovePrefix : sourceTargetDirRemovePrefixes )
402             {
403                 targetDir = targetDir.replaceFirst( "^" + Pattern.quote( sourceTargetDirRemovePrefix ), "" );
404             }
405         }
406         targetDir = targetDir.replaceFirst( "^/", "" );
407       
408         SvnExternalEntry external = null;
409         File f = new File( targetDir );
410         try
411         {
412             f.getCanonicalPath();
413         }
414         catch ( IOException e )
415         {
416             getLog().warn( "targetPath for external " + artifactToString( artifact ) + " is not a path : "
417                 + targetDir );
418             return external;
419         }
420         external = new SvnExternalEntry();
421                 
422         external.targetDir = targetDir;
423         
424         external.origin = StringUtils.equals( rootSvnInfo.getSvnRoot(), dependencySvnInfo.getSvnRoot() )
425             ? dependencySvnInfo.getSvnRelativeUrl() : dependencySvnInfo.getSvnUrl();
426     
427         external.revision = sourceFreezeRevision
428             ? "-r" + String.valueOf( dependencySvnInfo.getRevision() ) : null;
429 
430         return external;
431     }
432     
433 
434     SvnExternalEntry buildCurrentDependencyExternalFromPluginsConfig( Artifact artifact, MavenProject dependencyProject,
435        SvnInfo dependencySvnInfo, SvnInfo rootSvnInfo )
436     {
437         SvnExternalEntry external = null;
438         // Option 3 : if this.externals contains dependencyProject
439         if ( null != sourceTargets )
440         {
441             for ( SourceTarget curExt : sourceTargets )
442             {
443                 if ( curExt.dependencyMatch( artifact ) )
444                 {
445                     getLog().debug( "Dependency " +  curExt.dependency + " match " + artifactToString( artifact ) );
446                     external = buildExternalEntryFromProvidedInfos( curExt.targetDir, artifact,
447                         dependencyProject, dependencySvnInfo, rootSvnInfo );
448                     if ( null != external )
449                     {
450                         getLog().info( "Dependency " + artifactToString( artifact )
451                             + " external entry computed with plugin config is : " + external.toString() );
452                     }
453                     break;
454                 }
455             }
456         }
457         return external;
458     }
459     
460     SvnExternalEntry buildCurrentDependencyFromDependencyConfig( Artifact artifact, MavenProject dependencyProject,
461        SvnInfo dependencySvnInfo, SvnInfo rootSvnInfo )
462     {
463         SvnExternalEntry external = null;
464         // Option 2 : if dependencyProject.getProperties() contains 'scm.dependencies.source.targetDir'
465         Properties ps = dependencyProject.getProperties();
466         if ( null != ps )
467         {
468             String targetDir = ps.getProperty( "scm.dependencies.source.targetDir" );
469             if ( ! StringUtils.isEmpty( targetDir ) )
470             {
471                 external = buildExternalEntryFromProvidedInfos( targetDir, artifact,
472                     dependencyProject, dependencySvnInfo, rootSvnInfo );
473                 if ( null != external )
474                 {
475                     getLog().info( "Dependency " + artifactToString( artifact )
476                         + " external entry computed with 'scm.dependencies.source.targetDir' property is : "
477                         + external.toString() );
478                 }
479             }
480             else
481             {
482                 getLog().info( "Maven project of dependency " + artifactToString( artifact )
483                     + " do not define property 'scm.dependencies.source.targetDir'" );
484             }
485         }
486         else
487         {
488             getLog().info( "Maven project of dependency " + artifactToString( artifact ) + " has no properties" );
489         }
490         return external;
491     }
492     
493     SvnExternalEntry buildCurrentDependencyExternalDefault( Artifact artifact, MavenProject dependencyProject,
494        SvnInfo dependencySvnInfo, SvnInfo rootSvnInfo )
495     {
496         SvnExternalEntry external = null;
497         // Option 1 : use artifact.getGroupId().artifact.getArtifactId()
498         String targetDir = dependencyProject.getGroupId() + "." + dependencyProject.getArtifactId();
499         // replace prefix while target dir is in "dot form"
500         if ( null != sourceTargetDirRemovePrefixes )
501         {
502             for ( String sourceTargetDirRemovePrefix : sourceTargetDirRemovePrefixes )
503             {
504                 targetDir = targetDir.replaceFirst( "^" + Pattern.quote( sourceTargetDirRemovePrefix ), "" );
505             }
506         }
507         targetDir = targetDir.replaceAll( Pattern.quote( "." ), "/" );
508         
509         external = buildExternalEntryFromProvidedInfos( targetDir, artifact,
510             dependencyProject, dependencySvnInfo, rootSvnInfo );
511         if ( null != external )
512         {
513             getLog().info( "Dependency " + artifactToString( artifact ) + " defaut computed external entry is : "
514                 + external.toString() );
515         }
516         return external;
517     }
518     
519     public String artifactToString( Artifact artifact )
520     {
521         StringBuffer dependencyStr = new StringBuffer();
522         dependencyStr.append( artifact.getGroupId() );
523         dependencyStr.append( ":" );
524         dependencyStr.append( artifact.getArtifactId() );
525         dependencyStr.append( ":" );
526         dependencyStr.append( artifact.getVersion() );
527         dependencyStr.append( ":" );
528         dependencyStr.append( artifact.getType() );
529         return dependencyStr.toString();
530     }
531     
532     protected void doExecute()
533         throws MojoExecutionException
534     {
535         DependencyStatusSets dss = getDependencySets( true );
536         
537         // if we use svn, we need an externals dir
538         SvnInfo externalsSvnInfo = null;
539         SvnExternalsEntries externalsEntries = null;
540         File targetSourceDir = null;
541 
542         for ( Artifact artifact : dss.getResolvedDependencies() )
543         {
544             String dependencyStr = artifactToString( artifact );
545           
546             MavenProject dependencyProject = buildProjectFromArtifact( artifact );
547             Scm scm = dependencyProject.getScm();
548 
549             if ( scm == null )
550             {
551                 throw new MojoExecutionException( "No SCM specified for artifact " + dependencyStr );
552             }
553 
554             String scmUri = scm.getConnection();
555             if ( StringUtils.equals( connectionType, "developerConnection" ) )
556             {
557                 scmUri = scm.getDeveloperConnection();
558             }
559             
560             if ( StringUtils.isEmpty( scmUri ) )
561             {
562                 throw new MojoExecutionException( "No SCM Uri specified for artifact " + dependencyStr );
563             }
564             
565             String[] scmType = scmUri.split( ":", 3 );
566             
567             if ( scmType.length < 3 || ! StringUtils.equalsIgnoreCase( scmType[0], "scm" ) )
568             {
569                 throw new MojoExecutionException( "SCM Uri content invalide : " + scmUri );
570             }
571             
572             if ( StringUtils.equalsIgnoreCase( scmType[1], "svn" ) )
573             {
574                 String dependencySvnUri = scmType[2];
575                 
576                 // check svn::externals dir only once
577                 if ( null == externalsSvnInfo || null == externalsEntries )
578                 {
579                     targetSourceDir = new File( basedir.toString() + File.separator + sourceSubdir );
580                     if ( ! targetSourceDir.isDirectory() )
581                     {
582                         throw new MojoExecutionException( "Svn externals dir does not exists or is not a directory : "
583                             + targetSourceDir );
584                     }
585                     
586                     externalsSvnInfo = SvnService.getSvnInfo( basedir,
587                         null, targetSourceDir.getAbsolutePath(), getLog(), false );
588                     
589                     if ( ! externalsSvnInfo.isValide() )
590                     {
591                         throw new MojoExecutionException( "Svn info not available for externals dir : "
592                             + targetSourceDir );
593                     }
594                                      
595                     getLog().info( "Svn externals dir is '" + externalsSvnInfo.toString() + "'" );
596                     
597                     externalsEntries = SvnService.loadSvnExternals( basedir,
598                         null, targetSourceDir.getAbsolutePath(), getLog() );
599                     // externalsEntries not null here
600                     
601                     getLog().info( "Svn initial externals are '" + externalsEntries.values().toString() + "'" );
602                 }
603                 Credential credential = Credential.createCredential(
604                     dependencySvnUri, username, password, settingsServerId, settings, secDispatcher, getLog() );
605                     
606                 SvnInfo dependencySvnInfo = SvnService.getSvnInfo( basedir, credential, dependencySvnUri,
607                     getLog(), false );
608                 
609                 if ( ! dependencySvnInfo.isValide() )
610                 {
611                     throw new MojoExecutionException( "Svn info not available for dependency : "
612                         + dependencyStr + " at " + dependencySvnUri );
613                 }
614                 getLog().info( "Svn info for dependency : " + dependencyStr + " are " + dependencySvnInfo.toString() );
615                               
616                 SvnExternalEntry ee = buildCurrentDependencyExternalFromPluginsConfig( artifact, dependencyProject,
617                          dependencySvnInfo, externalsSvnInfo );
618                 if ( null == ee )
619                 {
620                     ee = buildCurrentDependencyFromDependencyConfig( artifact, dependencyProject,
621                          dependencySvnInfo, externalsSvnInfo );
622                 }
623                 if ( null == ee )
624                 {
625                     ee = buildCurrentDependencyExternalDefault( artifact, dependencyProject, dependencySvnInfo,
626                           externalsSvnInfo );
627                 }
628                 
629                 if ( null != ee )
630                 {
631                     externalsEntries.put( ee );
632                 }
633                 else
634                 {
635                     throw new MojoExecutionException( "Unable to build external for : " + dependencyStr );
636                 }
637             } 
638             else
639             {
640                 throw new MojoExecutionException( "SCM unsupported yet : " + scmType[1] );
641             }
642         }
643         if ( null != externalsEntries && null != targetSourceDir )
644         {
645             SvnService.writeSvnExternals( basedir, null, targetSourceDir.getAbsolutePath(),
646                 externalsEntries, project.getBuild().getDirectory(), getLog() );
647         }
648     }
649 }