001    package org.apache.maven.scm.manager;
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    
022    import org.apache.maven.scm.ScmBranch;
023    import org.apache.maven.scm.ScmBranchParameters;
024    import org.apache.maven.scm.ScmException;
025    import org.apache.maven.scm.ScmFileSet;
026    import org.apache.maven.scm.ScmTagParameters;
027    import org.apache.maven.scm.ScmVersion;
028    import org.apache.maven.scm.command.add.AddScmResult;
029    import org.apache.maven.scm.command.blame.BlameScmRequest;
030    import org.apache.maven.scm.command.blame.BlameScmResult;
031    import org.apache.maven.scm.command.branch.BranchScmResult;
032    import org.apache.maven.scm.command.changelog.ChangeLogScmRequest;
033    import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
034    import org.apache.maven.scm.command.checkin.CheckInScmResult;
035    import org.apache.maven.scm.command.checkout.CheckOutScmResult;
036    import org.apache.maven.scm.command.diff.DiffScmResult;
037    import org.apache.maven.scm.command.edit.EditScmResult;
038    import org.apache.maven.scm.command.export.ExportScmResult;
039    import org.apache.maven.scm.command.list.ListScmResult;
040    import org.apache.maven.scm.command.mkdir.MkdirScmResult;
041    import org.apache.maven.scm.command.remove.RemoveScmResult;
042    import org.apache.maven.scm.command.status.StatusScmResult;
043    import org.apache.maven.scm.command.tag.TagScmResult;
044    import org.apache.maven.scm.command.unedit.UnEditScmResult;
045    import org.apache.maven.scm.command.update.UpdateScmResult;
046    import org.apache.maven.scm.log.ScmLogger;
047    import org.apache.maven.scm.provider.ScmProvider;
048    import org.apache.maven.scm.provider.ScmProviderRepository;
049    import org.apache.maven.scm.provider.ScmUrlUtils;
050    import org.apache.maven.scm.repository.ScmRepository;
051    import org.apache.maven.scm.repository.ScmRepositoryException;
052    import org.apache.maven.scm.repository.UnknownRepositoryStructure;
053    
054    import java.io.File;
055    import java.util.ArrayList;
056    import java.util.Date;
057    import java.util.HashMap;
058    import java.util.List;
059    import java.util.Map;
060    import java.util.Map.Entry;
061    
062    /**
063     * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
064     * @author <a href="mailto:brett@apache.org">Brett Porter</a>
065     * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
066     * @version $Id: AbstractScmManager.java 1354687 2012-06-27 19:38:56Z olamy $
067     */
068    public abstract class AbstractScmManager
069        implements ScmManager
070    {
071        private Map<String, ScmProvider> scmProviders = new HashMap<String, ScmProvider>();
072    
073        private ScmLogger logger;
074    
075        private Map<String, String> userProviderTypes = new HashMap<String, String>();
076    
077        protected void setScmProviders( Map<String, ScmProvider> providers )
078        {
079            this.scmProviders = providers;
080        }
081    
082        /**
083         * @deprecated use {@link #setScmProvider(String, ScmProvider)} instead
084         */
085        protected void addScmProvider( String providerType, ScmProvider provider )
086        {
087            setScmProvider( providerType, provider );
088        }
089    
090        /**
091         * Set a provider to be used for a type of SCM.
092         * If there was already a designed provider for that type it will be replaced.
093         *
094         * @param providerType the type of SCM, eg. <code>svn</code>, <code>cvs</code>
095         * @param provider     the provider that will be used for that SCM type
096         */
097        public void setScmProvider( String providerType, ScmProvider provider )
098        {
099            scmProviders.put( providerType, provider );
100        }
101    
102        protected abstract ScmLogger getScmLogger();
103    
104        // ----------------------------------------------------------------------
105        // ScmManager Implementation
106        // ----------------------------------------------------------------------
107    
108        /**
109         * {@inheritDoc}
110         */
111        public ScmProvider getProviderByUrl( String scmUrl )
112            throws ScmRepositoryException, NoSuchScmProviderException
113        {
114            if ( scmUrl == null )
115            {
116                throw new NullPointerException( "The scm url cannot be null." );
117            }
118    
119            String providerType = ScmUrlUtils.getProvider( scmUrl );
120    
121            return getProviderByType( providerType );
122        }
123    
124        /**
125         * {@inheritDoc}
126         */
127        public void setScmProviderImplementation( String providerType, String providerImplementation )
128        {
129            userProviderTypes.put( providerType, providerImplementation );
130        }
131    
132        /**
133         * {@inheritDoc}
134         */
135        public ScmProvider getProviderByType( String providerType )
136            throws NoSuchScmProviderException
137        {
138            if ( logger == null )
139            {
140                logger = getScmLogger();
141    
142                for ( Entry<String, ScmProvider> entry : scmProviders.entrySet() )
143                {
144                    ScmProvider p = entry.getValue();
145    
146                    p.addListener( logger );
147                }
148            }
149    
150            String usedProviderType = System.getProperty( "maven.scm.provider." + providerType + ".implementation" );
151    
152            if ( usedProviderType == null )
153            {
154                if ( userProviderTypes.containsKey( providerType ) )
155                {
156                    usedProviderType = userProviderTypes.get( providerType );
157                }
158                else
159                {
160                    usedProviderType = providerType;
161                }
162            }
163    
164            ScmProvider scmProvider = scmProviders.get( usedProviderType );
165    
166            if ( scmProvider == null )
167            {
168                throw new NoSuchScmProviderException( usedProviderType );
169            }
170    
171            return scmProvider;
172        }
173    
174        /**
175         * {@inheritDoc}
176         */
177        public ScmProvider getProviderByRepository( ScmRepository repository )
178            throws NoSuchScmProviderException
179        {
180            return getProviderByType( repository.getProvider() );
181        }
182    
183        // ----------------------------------------------------------------------
184        // Repository
185        // ----------------------------------------------------------------------
186    
187        /**
188         * {@inheritDoc}
189         */
190        public ScmRepository makeScmRepository( String scmUrl )
191            throws ScmRepositoryException, NoSuchScmProviderException
192        {
193            if ( scmUrl == null )
194            {
195                throw new NullPointerException( "The scm url cannot be null." );
196            }
197    
198            char delimiter = ScmUrlUtils.getDelimiter( scmUrl ).charAt( 0 );
199    
200            String providerType = ScmUrlUtils.getProvider( scmUrl );
201    
202            ScmProvider provider = getProviderByType( providerType );
203    
204            String scmSpecificUrl = cleanScmUrl( scmUrl.substring( providerType.length() + 5 ) );
205    
206            ScmProviderRepository providerRepository = provider.makeProviderScmRepository( scmSpecificUrl, delimiter );
207    
208            return new ScmRepository( providerType, providerRepository );
209        }
210    
211        /**
212         * Clean the SCM url by removing all ../ in path
213         *
214         * @param scmUrl the SCM url
215         * @return the cleaned SCM url
216         */
217        protected String cleanScmUrl( String scmUrl )
218        {
219            if ( scmUrl == null )
220            {
221                throw new NullPointerException( "The scm url cannot be null." );
222            }
223    
224            String pathSeparator = "";
225    
226            int indexOfDoubleDot = -1;
227    
228            // Clean Unix path
229            if ( scmUrl.indexOf( "../" ) > 1 )
230            {
231                pathSeparator = "/";
232    
233                indexOfDoubleDot = scmUrl.indexOf( "../" );
234            }
235    
236            // Clean windows path
237            if ( scmUrl.indexOf( "..\\" ) > 1 )
238            {
239                pathSeparator = "\\";
240    
241                indexOfDoubleDot = scmUrl.indexOf( "..\\" );
242            }
243    
244            if ( indexOfDoubleDot > 1 )
245            {
246                int startOfTextToRemove = scmUrl.substring( 0, indexOfDoubleDot - 1 ).lastIndexOf( pathSeparator );
247    
248                String beginUrl = "";
249                if ( startOfTextToRemove >= 0 )
250                {
251                    beginUrl = scmUrl.substring( 0, startOfTextToRemove );
252                }
253    
254                String endUrl = scmUrl.substring( indexOfDoubleDot + 3 );
255    
256                scmUrl = beginUrl + pathSeparator + endUrl;
257    
258                // Check if we have other double dot
259                if ( scmUrl.indexOf( "../" ) > 1 || scmUrl.indexOf( "..\\" ) > 1 )
260                {
261                    scmUrl = cleanScmUrl( scmUrl );
262                }
263            }
264    
265            return scmUrl;
266        }
267    
268        /**
269         * {@inheritDoc}
270         */
271        public ScmRepository makeProviderScmRepository( String providerType, File path )
272            throws ScmRepositoryException, UnknownRepositoryStructure, NoSuchScmProviderException
273        {
274            if ( providerType == null )
275            {
276                throw new NullPointerException( "The provider type cannot be null." );
277            }
278    
279            ScmProvider provider = getProviderByType( providerType );
280    
281            ScmProviderRepository providerRepository = provider.makeProviderScmRepository( path );
282    
283            return new ScmRepository( providerType, providerRepository );
284        }
285    
286        /**
287         * {@inheritDoc}
288         */
289        public List<String> validateScmRepository( String scmUrl )
290        {
291            List<String> messages = new ArrayList<String>();
292    
293            messages.addAll( ScmUrlUtils.validate( scmUrl ) );
294    
295            String providerType = ScmUrlUtils.getProvider( scmUrl );
296    
297            ScmProvider provider;
298    
299            try
300            {
301                provider = getProviderByType( providerType );
302            }
303            catch ( NoSuchScmProviderException e )
304            {
305                messages.add( "No such provider installed '" + providerType + "'." );
306    
307                return messages;
308            }
309    
310            String scmSpecificUrl = cleanScmUrl( scmUrl.substring( providerType.length() + 5 ) );
311    
312            List<String> providerMessages =
313                provider.validateScmUrl( scmSpecificUrl, ScmUrlUtils.getDelimiter( scmUrl ).charAt( 0 ) );
314    
315            if ( providerMessages == null )
316            {
317                throw new RuntimeException( "The SCM provider cannot return null from validateScmUrl()." );
318            }
319    
320            messages.addAll( providerMessages );
321    
322            return messages;
323        }
324    
325        /**
326         * {@inheritDoc}
327         */
328        public AddScmResult add( ScmRepository repository, ScmFileSet fileSet )
329            throws ScmException
330        {
331            return this.getProviderByRepository( repository ).add( repository, fileSet );
332        }
333    
334        /**
335         * {@inheritDoc}
336         */
337        public AddScmResult add( ScmRepository repository, ScmFileSet fileSet, String message )
338            throws ScmException
339        {
340            return this.getProviderByRepository( repository ).add( repository, fileSet, message );
341        }
342    
343        /**
344         * {@inheritDoc}
345         */
346        public BranchScmResult branch( ScmRepository repository, ScmFileSet fileSet, String branchName )
347            throws ScmException
348        {
349            ScmBranchParameters scmBranchParameters = new ScmBranchParameters( "" );
350            return this.getProviderByRepository( repository ).branch( repository, fileSet, branchName,
351                                                                      scmBranchParameters );
352        }
353    
354        /**
355         * {@inheritDoc}
356         */
357        public BranchScmResult branch( ScmRepository repository, ScmFileSet fileSet, String branchName, String message )
358            throws ScmException
359        {
360            ScmBranchParameters scmBranchParameters = new ScmBranchParameters( message );
361            return this.getProviderByRepository( repository ).branch( repository, fileSet, branchName,
362                                                                      scmBranchParameters );
363        }
364    
365        /**
366         * {@inheritDoc}
367         */
368        public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, Date startDate, Date endDate,
369                                             int numDays, ScmBranch branch )
370            throws ScmException
371        {
372            return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startDate, endDate, numDays,
373                                                                         branch );
374        }
375    
376        /**
377         * {@inheritDoc}
378         */
379        public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, Date startDate, Date endDate,
380                                             int numDays, ScmBranch branch, String datePattern )
381            throws ScmException
382        {
383            return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startDate, endDate, numDays,
384                                                                         branch, datePattern );
385        }
386    
387        /**
388         * {@inheritDoc}
389         */
390        public ChangeLogScmResult changeLog( ChangeLogScmRequest scmRequest )
391            throws ScmException
392        {
393            return this.getProviderByRepository( scmRequest.getScmRepository() ).changeLog( scmRequest );
394        }
395    
396        /**
397         * {@inheritDoc}
398         */
399        public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, ScmVersion startVersion,
400                                             ScmVersion endVersion )
401            throws ScmException
402        {
403            return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startVersion, endVersion );
404        }
405    
406        /**
407         * {@inheritDoc}
408         */
409        public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, ScmVersion startRevision,
410                                             ScmVersion endRevision, String datePattern )
411            throws ScmException
412        {
413            return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startRevision, endRevision,
414                                                                         datePattern );
415        }
416    
417        /**
418         * {@inheritDoc}
419         */
420        public CheckInScmResult checkIn( ScmRepository repository, ScmFileSet fileSet, String message )
421            throws ScmException
422        {
423            return this.getProviderByRepository( repository ).checkIn( repository, fileSet, message );
424        }
425    
426        /**
427         * {@inheritDoc}
428         */
429        public CheckInScmResult checkIn( ScmRepository repository, ScmFileSet fileSet, ScmVersion revision, String message )
430            throws ScmException
431        {
432            return this.getProviderByRepository( repository ).checkIn( repository, fileSet, revision, message );
433        }
434    
435        /**
436         * {@inheritDoc}
437         */
438        public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet )
439            throws ScmException
440        {
441            return this.getProviderByRepository( repository ).checkOut( repository, fileSet );
442        }
443    
444        /**
445         * {@inheritDoc}
446         */
447        public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet, ScmVersion version )
448            throws ScmException
449        {
450            return this.getProviderByRepository( repository ).checkOut( repository, fileSet, version );
451        }
452    
453        /**
454         * {@inheritDoc}
455         */
456        public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet, boolean recursive )
457            throws ScmException
458        {
459            return this.getProviderByRepository( repository ).checkOut( repository, fileSet, recursive );
460        }
461    
462        /**
463         * {@inheritDoc}
464         */
465        public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
466                                           boolean recursive )
467            throws ScmException
468        {
469            return this.getProviderByRepository( repository ).checkOut( repository, fileSet, version, recursive );
470        }
471    
472        /**
473         * {@inheritDoc}
474         */
475        public DiffScmResult diff( ScmRepository repository, ScmFileSet fileSet, ScmVersion startVersion,
476                                   ScmVersion endVersion )
477            throws ScmException
478        {
479            return this.getProviderByRepository( repository ).diff( repository, fileSet, startVersion, endVersion );
480        }
481    
482        /**
483         * {@inheritDoc}
484         */
485        public EditScmResult edit( ScmRepository repository, ScmFileSet fileSet )
486            throws ScmException
487        {
488            return this.getProviderByRepository( repository ).edit( repository, fileSet );
489        }
490    
491        /**
492         * {@inheritDoc}
493         */
494        public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet )
495            throws ScmException
496        {
497            return this.getProviderByRepository( repository ).export( repository, fileSet );
498        }
499    
500        /**
501         * {@inheritDoc}
502         */
503        public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet, ScmVersion version )
504            throws ScmException
505        {
506            return this.getProviderByRepository( repository ).export( repository, fileSet, version );
507        }
508    
509        /**
510         * {@inheritDoc}
511         */
512        public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet, String outputDirectory )
513            throws ScmException
514        {
515            return this.getProviderByRepository( repository ).export( repository, fileSet, (ScmVersion) null,
516                                                                      outputDirectory );
517        }
518    
519        /**
520         * {@inheritDoc}
521         */
522        public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
523                                       String outputDirectory )
524            throws ScmException
525        {
526            return this.getProviderByRepository( repository ).export( repository, fileSet, version, outputDirectory );
527        }
528    
529        /**
530         * {@inheritDoc}
531         */
532        public ListScmResult list( ScmRepository repository, ScmFileSet fileSet, boolean recursive, ScmVersion version )
533            throws ScmException
534        {
535            return this.getProviderByRepository( repository ).list( repository, fileSet, recursive, version );
536        }
537    
538        /**
539         * {@inheritDoc}
540         */
541        public MkdirScmResult mkdir( ScmRepository repository, ScmFileSet fileSet, String message, boolean createInLocal )
542            throws ScmException
543        {
544            return this.getProviderByRepository( repository ).mkdir( repository, fileSet, message, createInLocal );
545        }
546    
547        /**
548         * {@inheritDoc}
549         */
550        public RemoveScmResult remove( ScmRepository repository, ScmFileSet fileSet, String message )
551            throws ScmException
552        {
553            return this.getProviderByRepository( repository ).remove( repository, fileSet, message );
554        }
555    
556        /**
557         * {@inheritDoc}
558         */
559        public StatusScmResult status( ScmRepository repository, ScmFileSet fileSet )
560            throws ScmException
561        {
562            return this.getProviderByRepository( repository ).status( repository, fileSet );
563        }
564    
565        /**
566         * {@inheritDoc}
567         */
568        public TagScmResult tag( ScmRepository repository, ScmFileSet fileSet, String tagName )
569            throws ScmException
570        {
571            return this.tag( repository, fileSet, tagName, "" );
572        }
573    
574        /**
575         * {@inheritDoc}
576         */
577        public TagScmResult tag( ScmRepository repository, ScmFileSet fileSet, String tagName, String message )
578            throws ScmException
579        {
580            ScmTagParameters scmTagParameters = new ScmTagParameters( message );
581            return this.getProviderByRepository( repository ).tag( repository, fileSet, tagName, scmTagParameters );
582        }
583    
584        /**
585         * {@inheritDoc}
586         */
587        public UnEditScmResult unedit( ScmRepository repository, ScmFileSet fileSet )
588            throws ScmException
589        {
590            return this.getProviderByRepository( repository ).unedit( repository, fileSet );
591        }
592    
593        /**
594         * {@inheritDoc}
595         */
596        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet )
597            throws ScmException
598        {
599            return this.getProviderByRepository( repository ).update( repository, fileSet );
600        }
601    
602        /**
603         * {@inheritDoc}
604         */
605        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version )
606            throws ScmException
607        {
608            return this.getProviderByRepository( repository ).update( repository, fileSet, version );
609        }
610    
611        /**
612         * {@inheritDoc}
613         */
614        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, boolean runChangelog )
615            throws ScmException
616        {
617            return this.getProviderByRepository( repository ).update( repository, fileSet, runChangelog );
618        }
619    
620        /**
621         * {@inheritDoc}
622         */
623        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
624                                       boolean runChangelog )
625            throws ScmException
626        {
627            return this.getProviderByRepository( repository ).update( repository, fileSet, version, runChangelog );
628        }
629    
630        /**
631         * {@inheritDoc}
632         */
633        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, String datePattern )
634            throws ScmException
635        {
636            return this.getProviderByRepository( repository ).update( repository, fileSet, (ScmVersion) null, datePattern );
637        }
638    
639        /**
640         * {@inheritDoc}
641         */
642        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
643                                       String datePattern )
644            throws ScmException
645        {
646            return this.getProviderByRepository( repository ).update( repository, fileSet, version, datePattern );
647        }
648    
649        /**
650         * {@inheritDoc}
651         */
652        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, Date lastUpdate )
653            throws ScmException
654        {
655            return this.getProviderByRepository( repository ).update( repository, fileSet, (ScmVersion) null, lastUpdate );
656        }
657    
658        /**
659         * {@inheritDoc}
660         */
661        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version, Date lastUpdate )
662            throws ScmException
663        {
664            return this.getProviderByRepository( repository ).update( repository, fileSet, version, lastUpdate );
665        }
666    
667        /**
668         * {@inheritDoc}
669         */
670        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, Date lastUpdate, String datePattern )
671            throws ScmException
672        {
673            return this.getProviderByRepository( repository ).update( repository, fileSet, (ScmVersion) null, lastUpdate,
674                                                                      datePattern );
675        }
676    
677        /**
678         * {@inheritDoc}
679         */
680        public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version, Date lastUpdate,
681                                       String datePattern )
682            throws ScmException
683        {
684            return this.getProviderByRepository( repository ).update( repository, fileSet, version, lastUpdate,
685                                                                      datePattern );
686        }
687    
688        /**
689         * {@inheritDoc}
690         */
691        public BlameScmResult blame( ScmRepository repository, ScmFileSet fileSet, String filename )
692            throws ScmException
693        {
694            return this.getProviderByRepository( repository ).blame( repository, fileSet, filename );
695        }
696    
697        public BlameScmResult blame( BlameScmRequest blameScmRequest )
698            throws ScmException
699        {
700            return this.getProviderByRepository( blameScmRequest.getScmRepository() ).blame( blameScmRequest );
701        }
702    }