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.ScmBranch; 029import org.apache.maven.scm.ScmBranchParameters; 030import org.apache.maven.scm.ScmException; 031import org.apache.maven.scm.ScmFileSet; 032import org.apache.maven.scm.ScmTagParameters; 033import org.apache.maven.scm.ScmVersion; 034import org.apache.maven.scm.command.add.AddScmResult; 035import org.apache.maven.scm.command.blame.BlameScmRequest; 036import org.apache.maven.scm.command.blame.BlameScmResult; 037import org.apache.maven.scm.command.branch.BranchScmResult; 038import org.apache.maven.scm.command.changelog.ChangeLogScmRequest; 039import org.apache.maven.scm.command.changelog.ChangeLogScmResult; 040import org.apache.maven.scm.command.checkin.CheckInScmResult; 041import org.apache.maven.scm.command.checkout.CheckOutScmResult; 042import org.apache.maven.scm.command.diff.DiffScmResult; 043import org.apache.maven.scm.command.edit.EditScmResult; 044import org.apache.maven.scm.command.export.ExportScmResult; 045import org.apache.maven.scm.command.list.ListScmResult; 046import org.apache.maven.scm.command.mkdir.MkdirScmResult; 047import org.apache.maven.scm.command.remove.RemoveScmResult; 048import org.apache.maven.scm.command.status.StatusScmResult; 049import org.apache.maven.scm.command.tag.TagScmResult; 050import org.apache.maven.scm.command.unedit.UnEditScmResult; 051import org.apache.maven.scm.command.update.UpdateScmResult; 052import org.apache.maven.scm.provider.ScmProvider; 053import org.apache.maven.scm.provider.ScmProviderRepository; 054import org.apache.maven.scm.provider.ScmUrlUtils; 055import org.apache.maven.scm.repository.ScmRepository; 056import org.apache.maven.scm.repository.ScmRepositoryException; 057import org.apache.maven.scm.repository.UnknownRepositoryStructure; 058import org.slf4j.Logger; 059import org.slf4j.LoggerFactory; 060 061import static java.util.Objects.requireNonNull; 062 063/** 064 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a> 065 * @author <a href="mailto:brett@apache.org">Brett Porter</a> 066 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> 067 */ 068public abstract class AbstractScmManager implements ScmManager { 069 protected final Logger logger = LoggerFactory.getLogger(getClass()); 070 071 private final Map<String, ScmProvider> scmProviders = new ConcurrentHashMap<>(); 072 073 private final Map<String, String> userProviderTypes = new ConcurrentHashMap<>(); 074 075 protected void setScmProviders(Map<String, ScmProvider> providers) { 076 requireNonNull(providers); 077 this.scmProviders.clear(); 078 // first provider must not be overwritten by subsequent ones if they are registered for the same key 079 providers.forEach(this.scmProviders::putIfAbsent); 080 } 081 082 /** 083 * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code> 084 * @param provider the provider that will be used for that SCM type 085 * @deprecated use {@link #setScmProvider(String, ScmProvider)} instead 086 */ 087 @Deprecated 088 protected void addScmProvider(String providerType, ScmProvider provider) { 089 setScmProvider(providerType, provider); 090 } 091 092 /** 093 * Set a provider to be used for a type of SCM. 094 * If there was already a designed provider for that type it will be replaced. 095 * 096 * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code> 097 * @param provider the provider that will be used for that SCM type 098 */ 099 @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}