001package org.apache.maven.scm.provider.svn;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.scm.CommandParameters;
023import org.apache.maven.scm.ScmException;
024import org.apache.maven.scm.ScmFileSet;
025import org.apache.maven.scm.ScmResult;
026import org.apache.maven.scm.command.add.AddScmResult;
027import org.apache.maven.scm.command.blame.BlameScmResult;
028import org.apache.maven.scm.command.branch.BranchScmResult;
029import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
030import org.apache.maven.scm.command.checkin.CheckInScmResult;
031import org.apache.maven.scm.command.checkout.CheckOutScmResult;
032import org.apache.maven.scm.command.diff.DiffScmResult;
033import org.apache.maven.scm.command.export.ExportScmResult;
034import org.apache.maven.scm.command.info.InfoScmResult;
035import org.apache.maven.scm.command.list.ListScmResult;
036import org.apache.maven.scm.command.mkdir.MkdirScmResult;
037import org.apache.maven.scm.command.remove.RemoveScmResult;
038import org.apache.maven.scm.command.status.StatusScmResult;
039import org.apache.maven.scm.command.tag.TagScmResult;
040import org.apache.maven.scm.command.update.UpdateScmResult;
041import org.apache.maven.scm.provider.AbstractScmProvider;
042import org.apache.maven.scm.provider.ScmProviderRepository;
043import org.apache.maven.scm.provider.svn.command.SvnCommand;
044import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
045import org.apache.maven.scm.provider.svn.util.SvnUtil;
046import org.apache.maven.scm.repository.ScmRepositoryException;
047import org.apache.maven.scm.repository.UnknownRepositoryStructure;
048import org.codehaus.plexus.util.StringUtils;
049
050import java.io.File;
051import java.util.ArrayList;
052import java.util.List;
053
054/**
055 * SCM Provider for Subversion
056 *
057 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
058 *
059 */
060public abstract class AbstractSvnScmProvider
061    extends AbstractScmProvider
062{
063    // ----------------------------------------------------------------------
064    //
065    // ----------------------------------------------------------------------
066
067    private static class ScmUrlParserResult
068    {
069        private List<String> messages = new ArrayList<String>();
070
071        private ScmProviderRepository repository;
072    }
073
074    // ----------------------------------------------------------------------
075    // ScmProvider Implementation
076    // ----------------------------------------------------------------------
077
078    /**
079     * {@inheritDoc}
080     */
081    public String getScmSpecificFilename()
082    {
083        return ".svn";
084    }
085
086    /**
087     * {@inheritDoc}
088     */
089    public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter )
090        throws ScmRepositoryException
091    {
092        ScmUrlParserResult result = parseScmUrl( scmSpecificUrl );
093
094        if ( result.messages.size() > 0 )
095        {
096            throw new ScmRepositoryException( "The scm url is invalid.", result.messages );
097        }
098
099        return result.repository;
100    }
101
102    /**
103     * {@inheritDoc}
104     */
105    public ScmProviderRepository makeProviderScmRepository( File path )
106        throws ScmRepositoryException, UnknownRepositoryStructure
107    {
108        if ( path == null )
109        {
110            throw new NullPointerException( "Path argument is null" );
111        }
112
113        if ( !path.isDirectory() )
114        {
115            throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." );
116        }
117
118        if ( !new File( path, ".svn" ).exists() )
119        {
120            throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a svn checkout directory." );
121        }
122
123        try
124        {
125            return makeProviderScmRepository( getRepositoryURL( path ), ':' );
126        }
127        catch ( ScmException e )
128        {
129            // XXX We should allow throwing of SCMException.
130            throw new ScmRepositoryException( "Error executing info command", e );
131        }
132    }
133
134    protected abstract String getRepositoryURL( File path )
135        throws ScmException;
136
137    /**
138     * {@inheritDoc}
139     */
140    public List<String> validateScmUrl( String scmSpecificUrl, char delimiter )
141    {
142        List<String> messages = new ArrayList<String>();
143        try
144        {
145            makeProviderScmRepository( scmSpecificUrl, delimiter );
146        }
147        catch ( ScmRepositoryException e )
148        {
149            messages = e.getValidationMessages();
150        }
151        return messages;
152    }
153
154    /**
155     * {@inheritDoc}
156     */
157    public String getScmType()
158    {
159        return "svn";
160    }
161
162    // ----------------------------------------------------------------------
163    //
164    // ----------------------------------------------------------------------
165
166    private ScmUrlParserResult parseScmUrl( String scmSpecificUrl )
167    {
168        ScmUrlParserResult result = new ScmUrlParserResult();
169
170        String url = scmSpecificUrl;
171
172        // ----------------------------------------------------------------------
173        // Do some sanity checking of the SVN url
174        // ----------------------------------------------------------------------
175
176        if ( url.startsWith( "file" ) )
177        {
178            if ( !url.startsWith( "file://" ) )
179            {
180                result.messages.add( "A svn 'file' url must be on the form 'file://[hostname]/'." );
181
182                return result;
183            }
184        }
185        else if ( url.startsWith( "https" ) )
186        {
187            if ( !url.startsWith( "https://" ) )
188            {
189                result.messages.add( "A svn 'http' url must be on the form 'https://'." );
190
191                return result;
192            }
193        }
194        else if ( url.startsWith( "http" ) )
195        {
196            if ( !url.startsWith( "http://" ) )
197            {
198                result.messages.add( "A svn 'http' url must be on the form 'http://'." );
199
200                return result;
201            }
202        }
203        // Support of tunnels: svn+xxx with xxx defined in subversion conf file
204        else if ( url.startsWith( "svn+" ) )
205        {
206            if ( url.indexOf( "://" ) < 0 )
207            {
208                result.messages.add( "A svn 'svn+xxx' url must be on the form 'svn+xxx://'." );
209
210                return result;
211            }
212            else
213            {
214                String tunnel = url.substring( "svn+".length(), url.indexOf( "://" ) );
215
216                //ssh is always an allowed tunnel
217                if ( !"ssh".equals( tunnel ) )
218                {
219                    SvnConfigFileReader reader = new SvnConfigFileReader();
220                    if ( SvnUtil.getSettings().getConfigDirectory() != null )
221                    {
222                        reader.setConfigDirectory( new File( SvnUtil.getSettings().getConfigDirectory() ) );
223                    }
224
225                    if ( StringUtils.isEmpty( reader.getProperty( "tunnels", tunnel ) ) )
226                    {
227                        result.messages.add(
228                            "The tunnel '" + tunnel + "' isn't defined in your subversion configuration file." );
229
230                        return result;
231                    }
232                }
233            }
234        }
235        else if ( url.startsWith( "svn" ) )
236        {
237            if ( !url.startsWith( "svn://" ) )
238            {
239                result.messages.add( "A svn 'svn' url must be on the form 'svn://'." );
240
241                return result;
242            }
243        }
244        else
245        {
246            result.messages.add( url + " url isn't a valid svn URL." );
247
248            return result;
249        }
250
251        result.repository = new SvnScmProviderRepository( url );
252
253        return result;
254    }
255
256    protected abstract SvnCommand getAddCommand();
257
258    /**
259     * {@inheritDoc}
260     */
261    public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
262        throws ScmException
263    {
264        return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters );
265    }
266
267    protected abstract SvnCommand getBranchCommand();
268
269    /**
270     * {@inheritDoc}
271     */
272    protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet,
273                                      CommandParameters parameters )
274        throws ScmException
275    {
276        return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters );
277    }
278
279    protected abstract SvnCommand getChangeLogCommand();
280
281    /**
282     * {@inheritDoc}
283     */
284    public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
285                                         CommandParameters parameters )
286        throws ScmException
287    {
288        return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters );
289    }
290
291    protected abstract SvnCommand getCheckInCommand();
292
293    /**
294     * {@inheritDoc}
295     */
296    public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
297                                     CommandParameters parameters )
298        throws ScmException
299    {
300        return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters );
301    }
302
303    protected abstract SvnCommand getCheckOutCommand();
304
305    /**
306     * {@inheritDoc}
307     */
308    public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
309                                       CommandParameters parameters )
310        throws ScmException
311    {
312        return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters );
313    }
314
315    protected abstract SvnCommand getDiffCommand();
316
317    /**
318     * {@inheritDoc}
319     */
320    public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
321        throws ScmException
322    {
323        return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters );
324    }
325
326    protected abstract SvnCommand getExportCommand();
327
328    /**
329     * {@inheritDoc}
330     */
331    protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet,
332                                      CommandParameters parameters )
333        throws ScmException
334    {
335        return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters );
336    }
337
338    protected abstract SvnCommand getRemoveCommand();
339
340    /**
341     * {@inheritDoc}
342     */
343    public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
344        throws ScmException
345    {
346        return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters );
347    }
348
349    protected abstract SvnCommand getStatusCommand();
350
351    /**
352     * {@inheritDoc}
353     */
354    public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
355        throws ScmException
356    {
357        return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters );
358    }
359
360    protected abstract SvnCommand getTagCommand();
361
362    /**
363     * {@inheritDoc}
364     */
365    public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
366        throws ScmException
367    {
368        return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters );
369    }
370
371    protected abstract SvnCommand getUpdateCommand();
372
373    /**
374     * {@inheritDoc}
375     */
376    public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
377        throws ScmException
378    {
379        return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters );
380    }
381
382    protected ScmResult executeCommand( SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet,
383                                        CommandParameters parameters )
384        throws ScmException
385    {
386        command.setLogger( getLogger() );
387
388        return command.execute( repository, fileSet, parameters );
389    }
390
391    protected abstract SvnCommand getListCommand();
392
393    /**
394     * {@inheritDoc}
395     */
396    public ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
397        throws ScmException
398    {
399        SvnCommand cmd = getListCommand();
400
401        return (ListScmResult) executeCommand( cmd, repository, fileSet, parameters );
402    }
403
404    protected abstract SvnCommand getInfoCommand();
405
406    public InfoScmResult info( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
407        throws ScmException
408    {
409        SvnCommand cmd = getInfoCommand();
410
411        return (InfoScmResult) executeCommand( cmd, repository, fileSet, parameters );
412    }
413
414    /**
415     * {@inheritDoc}
416     */
417    protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
418        throws ScmException
419    {
420        SvnCommand cmd = getBlameCommand();
421
422        return (BlameScmResult) executeCommand( cmd, repository, fileSet, parameters );
423    }
424
425    protected abstract SvnCommand getBlameCommand();
426
427    /**
428     * {@inheritDoc}
429     */
430    public MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
431        throws ScmException
432    {
433        SvnCommand cmd = getMkdirCommand();
434
435        return (MkdirScmResult) executeCommand( cmd, repository, fileSet, parameters );
436    }
437
438    protected abstract SvnCommand getMkdirCommand();
439
440    /**
441     * @param repository
442     * @param parameters
443     * @return true if remote url exists
444     * @throws ScmException
445     * @since 1.8
446     */
447    public abstract boolean remoteUrlExist( ScmProviderRepository repository, CommandParameters parameters )
448        throws ScmException;
449}