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