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