View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.scm.manager;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.Date;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.concurrent.ConcurrentHashMap;
27  
28  import org.apache.maven.scm.CommandParameters;
29  import org.apache.maven.scm.ScmBranch;
30  import org.apache.maven.scm.ScmBranchParameters;
31  import org.apache.maven.scm.ScmException;
32  import org.apache.maven.scm.ScmFileSet;
33  import org.apache.maven.scm.ScmTagParameters;
34  import org.apache.maven.scm.ScmVersion;
35  import org.apache.maven.scm.command.add.AddScmResult;
36  import org.apache.maven.scm.command.blame.BlameScmRequest;
37  import org.apache.maven.scm.command.blame.BlameScmResult;
38  import org.apache.maven.scm.command.branch.BranchScmResult;
39  import org.apache.maven.scm.command.changelog.ChangeLogScmRequest;
40  import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
41  import org.apache.maven.scm.command.checkin.CheckInScmResult;
42  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
43  import org.apache.maven.scm.command.diff.DiffScmResult;
44  import org.apache.maven.scm.command.edit.EditScmResult;
45  import org.apache.maven.scm.command.export.ExportScmResult;
46  import org.apache.maven.scm.command.list.ListScmResult;
47  import org.apache.maven.scm.command.mkdir.MkdirScmResult;
48  import org.apache.maven.scm.command.remove.RemoveScmResult;
49  import org.apache.maven.scm.command.status.StatusScmResult;
50  import org.apache.maven.scm.command.tag.TagScmResult;
51  import org.apache.maven.scm.command.unedit.UnEditScmResult;
52  import org.apache.maven.scm.command.update.UpdateScmResult;
53  import org.apache.maven.scm.provider.ScmProvider;
54  import org.apache.maven.scm.provider.ScmProviderRepository;
55  import org.apache.maven.scm.provider.ScmUrlUtils;
56  import org.apache.maven.scm.repository.ScmRepository;
57  import org.apache.maven.scm.repository.ScmRepositoryException;
58  import org.apache.maven.scm.repository.UnknownRepositoryStructure;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
61  
62  import static java.util.Objects.requireNonNull;
63  
64  /**
65   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
66   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
67   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
68   */
69  public abstract class AbstractScmManager implements ScmManager {
70      protected final Logger logger = LoggerFactory.getLogger(getClass());
71  
72      private final Map<String, ScmProvider> scmProviders = new ConcurrentHashMap<>();
73  
74      private final Map<String, String> userProviderTypes = new ConcurrentHashMap<>();
75  
76      protected void setScmProviders(Map<String, ScmProvider> providers) {
77          requireNonNull(providers);
78          this.scmProviders.clear();
79          // first provider must not be overwritten by subsequent ones if they are registered for the same key
80          providers.forEach(this.scmProviders::putIfAbsent);
81      }
82  
83      /**
84       * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code>
85       * @param provider     the provider that will be used for that SCM type
86       * @deprecated use {@link #setScmProvider(String, ScmProvider)} instead
87       */
88      @Deprecated
89      protected void addScmProvider(String providerType, ScmProvider provider) {
90          setScmProvider(providerType, provider);
91      }
92  
93      /**
94       * Set a provider to be used for a type of SCM.
95       * If there was already a designed provider for that type it will be replaced.
96       *
97       * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code>
98       * @param provider     the provider that will be used for that SCM type
99       */
100     @Override
101     public void setScmProvider(String providerType, ScmProvider provider) {
102         requireNonNull(providerType);
103         requireNonNull(provider);
104         scmProviders.put(providerType, provider);
105     }
106 
107     // ----------------------------------------------------------------------
108     // ScmManager Implementation
109     // ----------------------------------------------------------------------
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
115     public ScmProvider getProviderByUrl(String scmUrl) throws ScmRepositoryException, NoSuchScmProviderException {
116         requireNonNull(scmUrl, "The scm url cannot be null.");
117 
118         String providerType = ScmUrlUtils.getProvider(scmUrl);
119 
120         return getProviderByType(providerType);
121     }
122 
123     /**
124      * {@inheritDoc}
125      */
126     @Override
127     public void setScmProviderImplementation(String providerType, String providerImplementation) {
128         requireNonNull(providerType);
129         requireNonNull(providerImplementation);
130         userProviderTypes.put(providerType, providerImplementation);
131     }
132 
133     /**
134      * {@inheritDoc}
135      */
136     @Override
137     public ScmProvider getProviderByType(String providerType) throws NoSuchScmProviderException {
138         String usedProviderType = System.getProperty("maven.scm.provider." + providerType + ".implementation");
139 
140         if (usedProviderType == null) {
141             usedProviderType = userProviderTypes.getOrDefault(providerType, providerType);
142         }
143 
144         ScmProvider scmProvider = scmProviders.get(usedProviderType);
145 
146         if (scmProvider == null) {
147             throw new NoSuchScmProviderException(usedProviderType);
148         }
149 
150         return scmProvider;
151     }
152 
153     /**
154      * {@inheritDoc}
155      */
156     @Override
157     public ScmProvider getProviderByRepository(ScmRepository repository) throws NoSuchScmProviderException {
158         return getProviderByType(repository.getProvider());
159     }
160 
161     // ----------------------------------------------------------------------
162     // Repository
163     // ----------------------------------------------------------------------
164 
165     /**
166      * {@inheritDoc}
167      */
168     @Override
169     public ScmRepository makeScmRepository(String scmUrl) throws ScmRepositoryException, NoSuchScmProviderException {
170         requireNonNull(scmUrl, "The scm url cannot be null.");
171 
172         char delimiter = ScmUrlUtils.getDelimiter(scmUrl).charAt(0);
173 
174         String providerType = ScmUrlUtils.getProvider(scmUrl);
175 
176         ScmProvider provider = getProviderByType(providerType);
177 
178         String scmSpecificUrl = cleanScmUrl(scmUrl.substring(providerType.length() + 5));
179 
180         ScmProviderRepository providerRepository = provider.makeProviderScmRepository(scmSpecificUrl, delimiter);
181 
182         return new ScmRepository(providerType, providerRepository);
183     }
184 
185     /**
186      * Clean the SCM url by removing all ../ in path
187      *
188      * @param scmUrl the SCM url
189      * @return the cleaned SCM url
190      */
191     protected String cleanScmUrl(String scmUrl) {
192         requireNonNull(scmUrl, "The scm url cannot be null.");
193 
194         String pathSeparator = "";
195 
196         int indexOfDoubleDot = -1;
197 
198         // Clean Unix path
199         if (scmUrl.indexOf("../") > 1) {
200             pathSeparator = "/";
201 
202             indexOfDoubleDot = scmUrl.indexOf("../");
203         }
204 
205         // Clean windows path
206         if (scmUrl.indexOf("..\\") > 1) {
207             pathSeparator = "\\";
208 
209             indexOfDoubleDot = scmUrl.indexOf("..\\");
210         }
211 
212         if (indexOfDoubleDot > 1) {
213             int startOfTextToRemove = scmUrl.substring(0, indexOfDoubleDot - 1).lastIndexOf(pathSeparator);
214 
215             String beginUrl = "";
216             if (startOfTextToRemove >= 0) {
217                 beginUrl = scmUrl.substring(0, startOfTextToRemove);
218             }
219 
220             String endUrl = scmUrl.substring(indexOfDoubleDot + 3);
221 
222             scmUrl = beginUrl + pathSeparator + endUrl;
223 
224             // Check if we have other double dot
225             if (scmUrl.indexOf("../") > 1 || scmUrl.indexOf("..\\") > 1) {
226                 scmUrl = cleanScmUrl(scmUrl);
227             }
228         }
229 
230         return scmUrl;
231     }
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     public ScmRepository makeProviderScmRepository(String providerType, File path)
238             throws ScmRepositoryException, UnknownRepositoryStructure, NoSuchScmProviderException {
239         requireNonNull(providerType, "The provider type cannot be null.");
240 
241         ScmProvider provider = getProviderByType(providerType);
242 
243         ScmProviderRepository providerRepository = provider.makeProviderScmRepository(path);
244 
245         return new ScmRepository(providerType, providerRepository);
246     }
247 
248     /**
249      * {@inheritDoc}
250      */
251     @Override
252     public List<String> validateScmRepository(String scmUrl) {
253         List<String> messages = new ArrayList<>(ScmUrlUtils.validate(scmUrl));
254 
255         String providerType = ScmUrlUtils.getProvider(scmUrl);
256 
257         ScmProvider provider;
258 
259         try {
260             provider = getProviderByType(providerType);
261         } catch (NoSuchScmProviderException e) {
262             messages.add("No such provider installed '" + providerType + "'.");
263 
264             return messages;
265         }
266 
267         String scmSpecificUrl = cleanScmUrl(scmUrl.substring(providerType.length() + 5));
268 
269         List<String> providerMessages = provider.validateScmUrl(
270                 scmSpecificUrl, ScmUrlUtils.getDelimiter(scmUrl).charAt(0));
271 
272         requireNonNull(providerMessages, "The SCM provider cannot return null from validateScmUrl().");
273 
274         messages.addAll(providerMessages);
275 
276         return messages;
277     }
278 
279     /**
280      * {@inheritDoc}
281      */
282     @Override
283     public AddScmResult add(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
284         return this.getProviderByRepository(repository).add(repository, fileSet);
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     @Override
291     public AddScmResult add(ScmRepository repository, ScmFileSet fileSet, String message) throws ScmException {
292         return this.getProviderByRepository(repository).add(repository, fileSet, message);
293     }
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
299     public BranchScmResult branch(ScmRepository repository, ScmFileSet fileSet, String branchName) throws ScmException {
300         ScmBranchParameters scmBranchParameters = new ScmBranchParameters("");
301         return this.getProviderByRepository(repository).branch(repository, fileSet, branchName, scmBranchParameters);
302     }
303 
304     /**
305      * {@inheritDoc}
306      */
307     @Override
308     public BranchScmResult branch(ScmRepository repository, ScmFileSet fileSet, String branchName, String message)
309             throws ScmException {
310         ScmBranchParameters scmBranchParameters = new ScmBranchParameters(message);
311         return this.getProviderByRepository(repository).branch(repository, fileSet, branchName, scmBranchParameters);
312     }
313 
314     /**
315      * {@inheritDoc}
316      */
317     @Override
318     public ChangeLogScmResult changeLog(
319             ScmRepository repository, ScmFileSet fileSet, Date startDate, Date endDate, int numDays, ScmBranch branch)
320             throws ScmException {
321         return this.getProviderByRepository(repository)
322                 .changeLog(repository, fileSet, startDate, endDate, numDays, branch);
323     }
324 
325     /**
326      * {@inheritDoc}
327      */
328     @Override
329     public ChangeLogScmResult changeLog(
330             ScmRepository repository,
331             ScmFileSet fileSet,
332             Date startDate,
333             Date endDate,
334             int numDays,
335             ScmBranch branch,
336             String datePattern)
337             throws ScmException {
338         return this.getProviderByRepository(repository)
339                 .changeLog(repository, fileSet, startDate, endDate, numDays, branch, datePattern);
340     }
341 
342     /**
343      * {@inheritDoc}
344      */
345     @Override
346     public ChangeLogScmResult changeLog(ChangeLogScmRequest scmRequest) throws ScmException {
347         return this.getProviderByRepository(scmRequest.getScmRepository()).changeLog(scmRequest);
348     }
349 
350     /**
351      * {@inheritDoc}
352      */
353     @Override
354     public ChangeLogScmResult changeLog(
355             ScmRepository repository, ScmFileSet fileSet, ScmVersion startVersion, ScmVersion endVersion)
356             throws ScmException {
357         return this.getProviderByRepository(repository).changeLog(repository, fileSet, startVersion, endVersion);
358     }
359 
360     /**
361      * {@inheritDoc}
362      */
363     @Override
364     public ChangeLogScmResult changeLog(
365             ScmRepository repository,
366             ScmFileSet fileSet,
367             ScmVersion startRevision,
368             ScmVersion endRevision,
369             String datePattern)
370             throws ScmException {
371         return this.getProviderByRepository(repository)
372                 .changeLog(repository, fileSet, startRevision, endRevision, datePattern);
373     }
374 
375     /**
376      * {@inheritDoc}
377      */
378     @Override
379     public CheckInScmResult checkIn(ScmRepository repository, ScmFileSet fileSet, String message) throws ScmException {
380         return this.getProviderByRepository(repository).checkIn(repository, fileSet, message);
381     }
382 
383     /**
384      * {@inheritDoc}
385      */
386     @Override
387     public CheckInScmResult checkIn(ScmRepository repository, ScmFileSet fileSet, ScmVersion revision, String message)
388             throws ScmException {
389         return this.getProviderByRepository(repository).checkIn(repository, fileSet, revision, message);
390     }
391 
392     @Override
393     public CheckInScmResult checkIn(ScmRepository repository, ScmFileSet fileSet, CommandParameters commandParameters)
394             throws ScmException {
395         return this.getProviderByRepository(repository).checkIn(repository, fileSet, commandParameters);
396     }
397 
398     /**
399      * {@inheritDoc}
400      */
401     @Override
402     public CheckOutScmResult checkOut(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
403         return this.getProviderByRepository(repository).checkOut(repository, fileSet);
404     }
405 
406     /**
407      * {@inheritDoc}
408      */
409     @Override
410     public CheckOutScmResult checkOut(ScmRepository repository, ScmFileSet fileSet, ScmVersion version)
411             throws ScmException {
412         return this.getProviderByRepository(repository).checkOut(repository, fileSet, version);
413     }
414 
415     /**
416      * {@inheritDoc}
417      */
418     @Override
419     public CheckOutScmResult checkOut(ScmRepository repository, ScmFileSet fileSet, boolean recursive)
420             throws ScmException {
421         return this.getProviderByRepository(repository).checkOut(repository, fileSet, recursive);
422     }
423 
424     /**
425      * {@inheritDoc}
426      */
427     @Override
428     public CheckOutScmResult checkOut(
429             ScmRepository repository, ScmFileSet fileSet, ScmVersion version, boolean recursive) throws ScmException {
430         return this.getProviderByRepository(repository).checkOut(repository, fileSet, version, recursive);
431     }
432 
433     /**
434      * {@inheritDoc}
435      */
436     @Override
437     public DiffScmResult diff(
438             ScmRepository repository, ScmFileSet fileSet, ScmVersion startVersion, ScmVersion endVersion)
439             throws ScmException {
440         return this.getProviderByRepository(repository).diff(repository, fileSet, startVersion, endVersion);
441     }
442 
443     /**
444      * {@inheritDoc}
445      */
446     @Override
447     public EditScmResult edit(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
448         return this.getProviderByRepository(repository).edit(repository, fileSet);
449     }
450 
451     /**
452      * {@inheritDoc}
453      */
454     @Override
455     public ExportScmResult export(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
456         return this.getProviderByRepository(repository).export(repository, fileSet);
457     }
458 
459     /**
460      * {@inheritDoc}
461      */
462     @Override
463     public ExportScmResult export(ScmRepository repository, ScmFileSet fileSet, ScmVersion version)
464             throws ScmException {
465         return this.getProviderByRepository(repository).export(repository, fileSet, version);
466     }
467 
468     /**
469      * {@inheritDoc}
470      */
471     @Override
472     public ExportScmResult export(ScmRepository repository, ScmFileSet fileSet, String outputDirectory)
473             throws ScmException {
474         return this.getProviderByRepository(repository).export(repository, fileSet, (ScmVersion) null, outputDirectory);
475     }
476 
477     /**
478      * {@inheritDoc}
479      */
480     @Override
481     public ExportScmResult export(
482             ScmRepository repository, ScmFileSet fileSet, ScmVersion version, String outputDirectory)
483             throws ScmException {
484         return this.getProviderByRepository(repository).export(repository, fileSet, version, outputDirectory);
485     }
486 
487     /**
488      * {@inheritDoc}
489      */
490     @Override
491     public ListScmResult list(ScmRepository repository, ScmFileSet fileSet, boolean recursive, ScmVersion version)
492             throws ScmException {
493         return this.getProviderByRepository(repository).list(repository, fileSet, recursive, version);
494     }
495 
496     /**
497      * {@inheritDoc}
498      */
499     @Override
500     public MkdirScmResult mkdir(ScmRepository repository, ScmFileSet fileSet, String message, boolean createInLocal)
501             throws ScmException {
502         return this.getProviderByRepository(repository).mkdir(repository, fileSet, message, createInLocal);
503     }
504 
505     /**
506      * {@inheritDoc}
507      */
508     @Override
509     public RemoveScmResult remove(ScmRepository repository, ScmFileSet fileSet, String message) throws ScmException {
510         return this.getProviderByRepository(repository).remove(repository, fileSet, message);
511     }
512 
513     /**
514      * {@inheritDoc}
515      */
516     @Override
517     public StatusScmResult status(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
518         return this.getProviderByRepository(repository).status(repository, fileSet);
519     }
520 
521     /**
522      * {@inheritDoc}
523      */
524     @Override
525     public TagScmResult tag(ScmRepository repository, ScmFileSet fileSet, String tagName) throws ScmException {
526         return this.tag(repository, fileSet, tagName, "");
527     }
528 
529     /**
530      * {@inheritDoc}
531      */
532     @Override
533     public TagScmResult tag(ScmRepository repository, ScmFileSet fileSet, String tagName, String message)
534             throws ScmException {
535         ScmTagParameters scmTagParameters = new ScmTagParameters(message);
536         return this.getProviderByRepository(repository).tag(repository, fileSet, tagName, scmTagParameters);
537     }
538 
539     /**
540      * {@inheritDoc}
541      */
542     @Override
543     public UnEditScmResult unedit(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
544         return this.getProviderByRepository(repository).unedit(repository, fileSet);
545     }
546 
547     /**
548      * {@inheritDoc}
549      */
550     @Override
551     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet) throws ScmException {
552         return this.getProviderByRepository(repository).update(repository, fileSet);
553     }
554 
555     /**
556      * {@inheritDoc}
557      */
558     @Override
559     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, ScmVersion version)
560             throws ScmException {
561         return this.getProviderByRepository(repository).update(repository, fileSet, version);
562     }
563 
564     /**
565      * {@inheritDoc}
566      */
567     @Override
568     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, boolean runChangelog)
569             throws ScmException {
570         return this.getProviderByRepository(repository).update(repository, fileSet, runChangelog);
571     }
572 
573     /**
574      * {@inheritDoc}
575      */
576     @Override
577     public UpdateScmResult update(
578             ScmRepository repository, ScmFileSet fileSet, ScmVersion version, boolean runChangelog)
579             throws ScmException {
580         return this.getProviderByRepository(repository).update(repository, fileSet, version, runChangelog);
581     }
582 
583     /**
584      * {@inheritDoc}
585      */
586     @Override
587     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, String datePattern)
588             throws ScmException {
589         return this.getProviderByRepository(repository).update(repository, fileSet, (ScmVersion) null, datePattern);
590     }
591 
592     /**
593      * {@inheritDoc}
594      */
595     @Override
596     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, ScmVersion version, String datePattern)
597             throws ScmException {
598         return this.getProviderByRepository(repository).update(repository, fileSet, version, datePattern);
599     }
600 
601     /**
602      * {@inheritDoc}
603      */
604     @Override
605     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, Date lastUpdate) throws ScmException {
606         return this.getProviderByRepository(repository).update(repository, fileSet, (ScmVersion) null, lastUpdate);
607     }
608 
609     /**
610      * {@inheritDoc}
611      */
612     @Override
613     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, ScmVersion version, Date lastUpdate)
614             throws ScmException {
615         return this.getProviderByRepository(repository).update(repository, fileSet, version, lastUpdate);
616     }
617 
618     /**
619      * {@inheritDoc}
620      */
621     @Override
622     public UpdateScmResult update(ScmRepository repository, ScmFileSet fileSet, Date lastUpdate, String datePattern)
623             throws ScmException {
624         return this.getProviderByRepository(repository)
625                 .update(repository, fileSet, (ScmVersion) null, lastUpdate, datePattern);
626     }
627 
628     /**
629      * {@inheritDoc}
630      */
631     @Override
632     public UpdateScmResult update(
633             ScmRepository repository, ScmFileSet fileSet, ScmVersion version, Date lastUpdate, String datePattern)
634             throws ScmException {
635         return this.getProviderByRepository(repository).update(repository, fileSet, version, lastUpdate, datePattern);
636     }
637 
638     /**
639      * {@inheritDoc}
640      */
641     @Override
642     public BlameScmResult blame(ScmRepository repository, ScmFileSet fileSet, String filename) throws ScmException {
643         return this.getProviderByRepository(repository).blame(repository, fileSet, filename);
644     }
645 
646     @Override
647     public BlameScmResult blame(BlameScmRequest blameScmRequest) throws ScmException {
648         return this.getProviderByRepository(blameScmRequest.getScmRepository()).blame(blameScmRequest);
649     }
650 }