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