001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.manager; 020 021import java.io.File; 022import java.util.ArrayList; 023import java.util.Date; 024import java.util.List; 025import java.util.Map; 026import java.util.concurrent.ConcurrentHashMap; 027 028import org.apache.maven.scm.CommandParameters; 029import org.apache.maven.scm.ScmBranch; 030import org.apache.maven.scm.ScmBranchParameters; 031import org.apache.maven.scm.ScmException; 032import org.apache.maven.scm.ScmFileSet; 033import org.apache.maven.scm.ScmTagParameters; 034import org.apache.maven.scm.ScmVersion; 035import org.apache.maven.scm.command.add.AddScmResult; 036import org.apache.maven.scm.command.blame.BlameScmRequest; 037import org.apache.maven.scm.command.blame.BlameScmResult; 038import org.apache.maven.scm.command.branch.BranchScmResult; 039import org.apache.maven.scm.command.changelog.ChangeLogScmRequest; 040import org.apache.maven.scm.command.changelog.ChangeLogScmResult; 041import org.apache.maven.scm.command.checkin.CheckInScmResult; 042import org.apache.maven.scm.command.checkout.CheckOutScmResult; 043import org.apache.maven.scm.command.diff.DiffScmResult; 044import org.apache.maven.scm.command.edit.EditScmResult; 045import org.apache.maven.scm.command.export.ExportScmResult; 046import org.apache.maven.scm.command.list.ListScmResult; 047import org.apache.maven.scm.command.mkdir.MkdirScmResult; 048import org.apache.maven.scm.command.remove.RemoveScmResult; 049import org.apache.maven.scm.command.status.StatusScmResult; 050import org.apache.maven.scm.command.tag.TagScmResult; 051import org.apache.maven.scm.command.unedit.UnEditScmResult; 052import org.apache.maven.scm.command.update.UpdateScmResult; 053import org.apache.maven.scm.provider.ScmProvider; 054import org.apache.maven.scm.provider.ScmProviderRepository; 055import org.apache.maven.scm.provider.ScmUrlUtils; 056import org.apache.maven.scm.repository.ScmRepository; 057import org.apache.maven.scm.repository.ScmRepositoryException; 058import org.apache.maven.scm.repository.UnknownRepositoryStructure; 059import org.slf4j.Logger; 060import org.slf4j.LoggerFactory; 061 062import static java.util.Objects.requireNonNull; 063 064/** 065 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a> 066 * @author <a href="mailto:brett@apache.org">Brett Porter</a> 067 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> 068 */ 069public abstract class AbstractScmManager implements ScmManager { 070 protected final Logger logger = LoggerFactory.getLogger(getClass()); 071 072 private final Map<String, ScmProvider> scmProviders = new ConcurrentHashMap<>(); 073 074 private final Map<String, String> userProviderTypes = new ConcurrentHashMap<>(); 075 076 protected void setScmProviders(Map<String, ScmProvider> providers) { 077 requireNonNull(providers); 078 this.scmProviders.clear(); 079 // first provider must not be overwritten by subsequent ones if they are registered for the same key 080 providers.forEach(this.scmProviders::putIfAbsent); 081 } 082 083 /** 084 * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code> 085 * @param provider the provider that will be used for that SCM type 086 * @deprecated use {@link #setScmProvider(String, ScmProvider)} instead 087 */ 088 @Deprecated 089 protected void addScmProvider(String providerType, ScmProvider provider) { 090 setScmProvider(providerType, provider); 091 } 092 093 /** 094 * Set a provider to be used for a type of SCM. 095 * If there was already a designed provider for that type it will be replaced. 096 * 097 * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code> 098 * @param provider the provider that will be used for that SCM type 099 */ 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}