View Javadoc
1   package org.apache.maven.scm.provider.svn;
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.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.maven.scm.CommandParameters;
27  import org.apache.maven.scm.ScmException;
28  import org.apache.maven.scm.ScmFileSet;
29  import org.apache.maven.scm.ScmResult;
30  import org.apache.maven.scm.command.add.AddScmResult;
31  import org.apache.maven.scm.command.blame.BlameScmResult;
32  import org.apache.maven.scm.command.branch.BranchScmResult;
33  import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
34  import org.apache.maven.scm.command.checkin.CheckInScmResult;
35  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
36  import org.apache.maven.scm.command.diff.DiffScmResult;
37  import org.apache.maven.scm.command.export.ExportScmResult;
38  import org.apache.maven.scm.command.info.InfoItem;
39  import org.apache.maven.scm.command.info.InfoScmResult;
40  import org.apache.maven.scm.command.list.ListScmResult;
41  import org.apache.maven.scm.command.mkdir.MkdirScmResult;
42  import org.apache.maven.scm.command.remove.RemoveScmResult;
43  import org.apache.maven.scm.command.status.StatusScmResult;
44  import org.apache.maven.scm.command.tag.TagScmResult;
45  import org.apache.maven.scm.command.update.UpdateScmResult;
46  import org.apache.maven.scm.provider.AbstractScmProvider;
47  import org.apache.maven.scm.provider.ScmProviderRepository;
48  import org.apache.maven.scm.provider.svn.command.SvnCommand;
49  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
50  import org.apache.maven.scm.provider.svn.util.SvnUtil;
51  import org.apache.maven.scm.repository.ScmRepositoryException;
52  import org.apache.maven.scm.repository.UnknownRepositoryStructure;
53  import org.codehaus.plexus.util.StringUtils;
54  
55  /**
56   * SCM Provider for Subversion
57   *
58   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
59   *
60   */
61  public abstract class AbstractSvnScmProvider
62      extends AbstractScmProvider
63  {
64      // ----------------------------------------------------------------------
65      //
66      // ----------------------------------------------------------------------
67  
68      private static class ScmUrlParserResult
69      {
70          private List<String> messages = new ArrayList<String>();
71  
72          private ScmProviderRepository repository;
73      }
74  
75      private static final String CHECK_WORKING_DIRECTORY_URL = "scmCheckWorkingDirectoryUrl";
76  
77      // ----------------------------------------------------------------------
78      // ScmProvider Implementation
79      // ----------------------------------------------------------------------
80  
81      /**
82       * {@inheritDoc}
83       */
84      public String getScmSpecificFilename()
85      {
86          return ".svn";
87      }
88  
89      /**
90       * {@inheritDoc}
91       */
92      public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter )
93          throws ScmRepositoryException
94      {
95          ScmUrlParserResult result = parseScmUrl( scmSpecificUrl );
96  
97          if ( checkWorkingDirectoryUrl() )
98          {
99              getLogger().debug( "Checking svn info 'URL:' field matches current sources directory" );
100             try
101             {
102                 String workingDir = System.getProperty( "scmCheckWorkingDirectoryUrl.currentWorkingDirectory" );
103                 InfoScmResult info =
104                     info( result.repository, new ScmFileSet( new File( workingDir ) ), new CommandParameters() );
105 
106                 String url = findUrlInfoItem( info );
107                 String comparison = "'" + url + "' vs. '" + scmSpecificUrl + "'";
108                 getLogger().debug( "Comparing : " + comparison );
109                 if ( url != null && !url.equals( scmSpecificUrl ) )
110                 {
111                     result.messages.add( "Scm url does not match the value returned by svn info (" + comparison + ")" );
112                 }
113             }
114             catch ( ScmException e )
115             {
116                 throw new ScmRepositoryException( "An error occurred while trying to svn info", e );
117             }
118         }
119         if ( result.messages.size() > 0 )
120         {
121             throw new ScmRepositoryException( "The scm url is invalid.", result.messages );
122         }
123 
124 
125         return result.repository;
126     }
127 
128     private boolean checkWorkingDirectoryUrl()
129     {
130         return Boolean.getBoolean( CHECK_WORKING_DIRECTORY_URL );
131     }
132 
133     private String findUrlInfoItem( InfoScmResult infoScmResult )
134     {
135         for ( InfoItem infoItem : infoScmResult.getInfoItems() )
136         {
137             if ( infoItem.getURL() != null )
138             {
139                 getLogger().debug( "URL found: " + infoItem.getURL() );
140                 return infoItem.getURL();
141             }
142         }
143         getLogger().debug( "URL not found (command output=" + infoScmResult.getCommandOutput() + ")" );
144         return null;
145     }
146 
147     /**
148      * {@inheritDoc}
149      */
150     public ScmProviderRepository makeProviderScmRepository( File path )
151         throws ScmRepositoryException, UnknownRepositoryStructure
152     {
153         if ( path == null )
154         {
155             throw new NullPointerException( "Path argument is null" );
156         }
157 
158         if ( !path.isDirectory() )
159         {
160             throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." );
161         }
162 
163         if ( !new File( path, ".svn" ).exists() )
164         {
165             throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a svn checkout directory." );
166         }
167 
168         try
169         {
170             return makeProviderScmRepository( getRepositoryURL( path ), ':' );
171         }
172         catch ( ScmException e )
173         {
174             // XXX We should allow throwing of SCMException.
175             throw new ScmRepositoryException( "Error executing info command", e );
176         }
177     }
178 
179     protected abstract String getRepositoryURL( File path )
180         throws ScmException;
181 
182     /**
183      * {@inheritDoc}
184      */
185     public List<String> validateScmUrl( String scmSpecificUrl, char delimiter )
186     {
187         List<String> messages = new ArrayList<String>();
188         try
189         {
190             makeProviderScmRepository( scmSpecificUrl, delimiter );
191         }
192         catch ( ScmRepositoryException e )
193         {
194             messages = e.getValidationMessages();
195         }
196         return messages;
197     }
198 
199     /**
200      * {@inheritDoc}
201      */
202     public String getScmType()
203     {
204         return "svn";
205     }
206 
207     // ----------------------------------------------------------------------
208     //
209     // ----------------------------------------------------------------------
210 
211     private ScmUrlParserResult parseScmUrl( String scmSpecificUrl )
212     {
213         ScmUrlParserResult result = new ScmUrlParserResult();
214 
215         String url = scmSpecificUrl;
216 
217         // ----------------------------------------------------------------------
218         // Do some sanity checking of the SVN url
219         // ----------------------------------------------------------------------
220 
221         if ( url.startsWith( "file" ) )
222         {
223             if ( !url.startsWith( "file://" ) )
224             {
225                 result.messages.add( "A svn 'file' url must be on the form 'file://[hostname]/'." );
226 
227                 return result;
228             }
229         }
230         else if ( url.startsWith( "https" ) )
231         {
232             if ( !url.startsWith( "https://" ) )
233             {
234                 result.messages.add( "A svn 'http' url must be on the form 'https://'." );
235 
236                 return result;
237             }
238         }
239         else if ( url.startsWith( "http" ) )
240         {
241             if ( !url.startsWith( "http://" ) )
242             {
243                 result.messages.add( "A svn 'http' url must be on the form 'http://'." );
244 
245                 return result;
246             }
247         }
248         // Support of tunnels: svn+xxx with xxx defined in subversion conf file
249         else if ( url.startsWith( "svn+" ) )
250         {
251             if ( url.indexOf( "://" ) < 0 )
252             {
253                 result.messages.add( "A svn 'svn+xxx' url must be on the form 'svn+xxx://'." );
254 
255                 return result;
256             }
257             else
258             {
259                 String tunnel = url.substring( "svn+".length(), url.indexOf( "://" ) );
260 
261                 //ssh is always an allowed tunnel
262                 if ( !"ssh".equals( tunnel ) )
263                 {
264                     SvnConfigFileReader reader = new SvnConfigFileReader();
265                     if ( SvnUtil.getSettings().getConfigDirectory() != null )
266                     {
267                         reader.setConfigDirectory( new File( SvnUtil.getSettings().getConfigDirectory() ) );
268                     }
269 
270                     if ( StringUtils.isEmpty( reader.getProperty( "tunnels", tunnel ) ) )
271                     {
272                         result.messages.add(
273                             "The tunnel '" + tunnel + "' isn't defined in your subversion configuration file." );
274 
275                         return result;
276                     }
277                 }
278             }
279         }
280         else if ( url.startsWith( "svn" ) )
281         {
282             if ( !url.startsWith( "svn://" ) )
283             {
284                 result.messages.add( "A svn 'svn' url must be on the form 'svn://'." );
285 
286                 return result;
287             }
288         }
289         else
290         {
291             result.messages.add( url + " url isn't a valid svn URL." );
292 
293             return result;
294         }
295 
296         result.repository = new SvnScmProviderRepository( url );
297 
298         return result;
299     }
300 
301     protected abstract SvnCommand getAddCommand();
302 
303     /**
304      * {@inheritDoc}
305      */
306     public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
307         throws ScmException
308     {
309         return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters );
310     }
311 
312     protected abstract SvnCommand getBranchCommand();
313 
314     /**
315      * {@inheritDoc}
316      */
317     protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet,
318                                       CommandParameters parameters )
319         throws ScmException
320     {
321         return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters );
322     }
323 
324     protected abstract SvnCommand getChangeLogCommand();
325 
326     /**
327      * {@inheritDoc}
328      */
329     public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
330                                          CommandParameters parameters )
331         throws ScmException
332     {
333         return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters );
334     }
335 
336     protected abstract SvnCommand getCheckInCommand();
337 
338     /**
339      * {@inheritDoc}
340      */
341     public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
342                                      CommandParameters parameters )
343         throws ScmException
344     {
345         return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters );
346     }
347 
348     protected abstract SvnCommand getCheckOutCommand();
349 
350     /**
351      * {@inheritDoc}
352      */
353     public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
354                                        CommandParameters parameters )
355         throws ScmException
356     {
357         return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters );
358     }
359 
360     protected abstract SvnCommand getDiffCommand();
361 
362     /**
363      * {@inheritDoc}
364      */
365     public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
366         throws ScmException
367     {
368         return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters );
369     }
370 
371     protected abstract SvnCommand getExportCommand();
372 
373     /**
374      * {@inheritDoc}
375      */
376     protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet,
377                                       CommandParameters parameters )
378         throws ScmException
379     {
380         return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters );
381     }
382 
383     protected abstract SvnCommand getRemoveCommand();
384 
385     /**
386      * {@inheritDoc}
387      */
388     public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
389         throws ScmException
390     {
391         return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters );
392     }
393 
394     protected abstract SvnCommand getStatusCommand();
395 
396     /**
397      * {@inheritDoc}
398      */
399     public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
400         throws ScmException
401     {
402         return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters );
403     }
404 
405     protected abstract SvnCommand getTagCommand();
406 
407     /**
408      * {@inheritDoc}
409      */
410     public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
411         throws ScmException
412     {
413         return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters );
414     }
415 
416     protected abstract SvnCommand getUpdateCommand();
417 
418     /**
419      * {@inheritDoc}
420      */
421     public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
422         throws ScmException
423     {
424         return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters );
425     }
426 
427     protected ScmResult executeCommand( SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet,
428                                         CommandParameters parameters )
429         throws ScmException
430     {
431         command.setLogger( getLogger() );
432 
433         return command.execute( repository, fileSet, parameters );
434     }
435 
436     protected abstract SvnCommand getListCommand();
437 
438     /**
439      * {@inheritDoc}
440      */
441     public ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
442         throws ScmException
443     {
444         SvnCommand cmd = getListCommand();
445 
446         return (ListScmResult) executeCommand( cmd, repository, fileSet, parameters );
447     }
448 
449     protected abstract SvnCommand getInfoCommand();
450 
451     public InfoScmResult info( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
452         throws ScmException
453     {
454         SvnCommand cmd = getInfoCommand();
455 
456         return (InfoScmResult) executeCommand( cmd, repository, fileSet, parameters );
457     }
458 
459     /**
460      * {@inheritDoc}
461      */
462     protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
463         throws ScmException
464     {
465         SvnCommand cmd = getBlameCommand();
466 
467         return (BlameScmResult) executeCommand( cmd, repository, fileSet, parameters );
468     }
469 
470     protected abstract SvnCommand getBlameCommand();
471 
472     /**
473      * {@inheritDoc}
474      */
475     public MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
476         throws ScmException
477     {
478         SvnCommand cmd = getMkdirCommand();
479 
480         return (MkdirScmResult) executeCommand( cmd, repository, fileSet, parameters );
481     }
482 
483     protected abstract SvnCommand getMkdirCommand();
484 
485     /**
486      * @param repository
487      * @param parameters
488      * @return true if remote url exists
489      * @throws ScmException
490      * @since 1.8
491      */
492     public abstract boolean remoteUrlExist( ScmProviderRepository repository, CommandParameters parameters )
493         throws ScmException;
494 }