001package org.apache.archiva.configuration; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.configuration.functors.ProxyConnectorConfigurationOrderComparator; 023import org.apache.archiva.configuration.io.registry.ConfigurationRegistryReader; 024import org.apache.archiva.configuration.io.registry.ConfigurationRegistryWriter; 025import org.apache.archiva.policies.AbstractUpdatePolicy; 026import org.apache.archiva.policies.CachedFailuresPolicy; 027import org.apache.archiva.policies.ChecksumPolicy; 028import org.apache.archiva.components.evaluator.DefaultExpressionEvaluator; 029import org.apache.archiva.components.evaluator.EvaluatorException; 030import org.apache.archiva.components.evaluator.ExpressionEvaluator; 031import org.apache.archiva.components.evaluator.sources.SystemPropertyExpressionSource; 032import org.apache.archiva.components.registry.Registry; 033import org.apache.archiva.components.registry.RegistryException; 034import org.apache.archiva.components.registry.RegistryListener; 035import org.apache.archiva.components.registry.commons.CommonsConfigurationRegistry; 036import org.apache.commons.collections4.CollectionUtils; 037import org.apache.commons.collections4.ListUtils; 038import org.apache.commons.configuration.BaseConfiguration; 039import org.apache.commons.io.FileUtils; 040import org.apache.commons.lang3.StringUtils; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043import org.springframework.stereotype.Service; 044 045import javax.annotation.PostConstruct; 046import javax.inject.Inject; 047import javax.inject.Named; 048import java.io.IOException; 049import java.nio.file.Files; 050import java.nio.file.Path; 051import java.nio.file.Paths; 052import java.util.*; 053import java.util.Map.Entry; 054 055/** 056 * <p> 057 * Implementation of configuration holder that retrieves it from the registry. 058 * </p> 059 * <p> 060 * The registry layers and merges the 2 configuration files: user, and application server. 061 * </p> 062 * <p> 063 * Instead of relying on the model defaults, if the registry is empty a default configuration file is loaded and 064 * applied from a resource. The defaults are not loaded into the registry as the lists (eg repositories) could no longer 065 * be removed if that was the case. 066 * </p> 067 * <p> 068 * When saving the configuration, it is saved to the location it was read from. If it was read from the defaults, it 069 * will be saved to the user location. 070 * However, if the configuration contains information from both sources, an exception is raised as this is currently 071 * unsupported. The reason for this is that it is not possible to identify where to re-save elements, and can result 072 * in list configurations (eg repositories) becoming inconsistent. 073 * </p> 074 * <p> 075 * If the configuration is outdated, it will be upgraded when it is loaded. This is done by checking the version flag 076 * before reading it from the registry. 077 * <p> 078 * FIXME: The synchronization must be improved, the current impl may lead to inconsistent data or multiple getConfiguration() calls (martin_s@apache.org) 079 * </p> 080 */ 081@Service("archivaConfiguration#default") 082public class DefaultArchivaConfiguration 083 implements ArchivaConfiguration, RegistryListener { 084 private final Logger log = LoggerFactory.getLogger(DefaultArchivaConfiguration.class); 085 086 private static String FILE_ENCODING = "UTF-8"; 087 088 /** 089 * Plexus registry to read the configuration from. 090 */ 091 @Inject 092 @Named(value = "commons-configuration") 093 private Registry registry; 094 095 /** 096 * The configuration that has been converted. 097 */ 098 private Configuration configuration; 099 100 /** 101 * see #initialize 102 * default-value="${user.home}/.m2/archiva.xml" 103 */ 104 private String userConfigFilename = "${user.home}/.m2/archiva.xml"; 105 106 /** 107 * see #initialize 108 * default-value="${appserver.base}/conf/archiva.xml" 109 */ 110 private String altConfigFilename = "${appserver.base}/conf/archiva.xml"; 111 112 /** 113 * Configuration Listeners we've registered. 114 */ 115 private Set<ConfigurationListener> listeners = new HashSet<>(); 116 117 /** 118 * Registry Listeners we've registered. 119 */ 120 private Set<RegistryListener> registryListeners = new HashSet<>(); 121 122 /** 123 * Boolean to help determine if the configuration exists as a result of pulling in 124 * the default-archiva.xml 125 */ 126 private boolean isConfigurationDefaulted = false; 127 128 private static final String KEY = "org.apache.archiva"; 129 130 // Section used for default only configuration 131 private static final String KEY_DEFAULT_ONLY = "org.apache.archiva_default"; 132 133 private Locale defaultLocale = Locale.getDefault(); 134 135 private List<Locale.LanguageRange> languagePriorities = new ArrayList<>(); 136 137 private volatile Path dataDirectory; 138 private volatile Path repositoryBaseDirectory; 139 private volatile Path remoteRepositoryBaseDirectory; 140 private volatile Path repositoryGroupBaseDirectory; 141 142 @PostConstruct 143 private void init() { 144 languagePriorities = Locale.LanguageRange.parse("en,fr,de"); 145 } 146 147 148 @Override 149 public Configuration getConfiguration() { 150 return loadConfiguration(); 151 } 152 153 private synchronized Configuration loadConfiguration() { 154 if (configuration == null) { 155 configuration = load(); 156 configuration = unescapeExpressions(configuration); 157 if (isConfigurationDefaulted) { 158 configuration = checkRepositoryLocations(configuration); 159 } 160 } 161 162 return configuration; 163 } 164 165 private boolean hasConfigVersionChanged(Configuration current, Registry defaultOnlyConfiguration) { 166 return current == null || current.getVersion() == null || 167 !current.getVersion().trim().equals(defaultOnlyConfiguration.getString("version", "").trim()); 168 } 169 170 @SuppressWarnings("unchecked") 171 private Configuration load() { 172 // TODO: should this be the same as section? make sure unnamed sections still work (eg, sys properties) 173 Registry subset = registry.getSubset(KEY); 174 if (subset.getString("version") == null) { 175 if (subset.getSubset("repositoryScanning").isEmpty()) { 176 // only for empty 177 subset = readDefaultConfiguration(); 178 } else { 179 throw new RuntimeException("No version tag found in configuration. Archiva configuration version 1.x is not longer supported."); 180 } 181 } 182 183 Configuration config = new ConfigurationRegistryReader().read(subset); 184 185 // Resolving data and repositories directories 186 // If the config entries are absolute, the path is used as it is 187 // if the config entries are empty, they are resolved: 188 // dataDirectory = ${appserver.base}/data 189 // repositoryDirectory = ${dataDirectory}/repositories 190 // If the entries are relative they are resolved 191 // relative to the appserver.base, for dataDirectory 192 // relative to dataDirectory for repositoryBase 193 String dataDir = config.getArchivaRuntimeConfiguration().getDataDirectory(); 194 if (StringUtils.isEmpty(dataDir)) { 195 dataDirectory = getAppServerBaseDir().resolve("data"); 196 } else { 197 Path tmpDataDir = Paths.get(dataDir); 198 if (tmpDataDir.isAbsolute()) { 199 dataDirectory = tmpDataDir; 200 } else { 201 dataDirectory = getAppServerBaseDir().resolve(tmpDataDir); 202 } 203 } 204 config.getArchivaRuntimeConfiguration().setDataDirectory(dataDirectory.normalize().toString()); 205 String repoBaseDir = config.getArchivaRuntimeConfiguration().getRepositoryBaseDirectory(); 206 if (StringUtils.isEmpty(repoBaseDir)) { 207 repositoryBaseDirectory = dataDirectory.resolve("repositories"); 208 209 } else { 210 Path tmpRepoBaseDir = Paths.get(repoBaseDir); 211 if (tmpRepoBaseDir.isAbsolute()) { 212 repositoryBaseDirectory = tmpRepoBaseDir; 213 } else { 214 dataDirectory.resolve(tmpRepoBaseDir); 215 } 216 } 217 218 String remoteRepoBaseDir = config.getArchivaRuntimeConfiguration().getRemoteRepositoryBaseDirectory(); 219 if (StringUtils.isEmpty(remoteRepoBaseDir)) { 220 remoteRepositoryBaseDirectory = dataDirectory.resolve("remotes"); 221 } else { 222 Path tmpRemoteRepoDir = Paths.get(remoteRepoBaseDir); 223 if (tmpRemoteRepoDir.isAbsolute()) { 224 remoteRepositoryBaseDirectory = tmpRemoteRepoDir; 225 } else { 226 dataDirectory.resolve(tmpRemoteRepoDir); 227 } 228 } 229 230 String repositoryGroupBaseDir = config.getArchivaRuntimeConfiguration().getRepositoryGroupBaseDirectory(); 231 if (StringUtils.isEmpty(repositoryGroupBaseDir)) { 232 repositoryGroupBaseDirectory = dataDirectory.resolve("groups"); 233 } else { 234 Path tmpGroupDir = Paths.get(repositoryGroupBaseDir); 235 if (tmpGroupDir.isAbsolute()) { 236 repositoryGroupBaseDirectory = tmpGroupDir; 237 } else { 238 dataDirectory.resolve(tmpGroupDir); 239 } 240 } 241 242 243 config.getRepositoryGroups(); 244 config.getRepositoryGroupsAsMap(); 245 if (!CollectionUtils.isEmpty(config.getRemoteRepositories())) { 246 List<RemoteRepositoryConfiguration> remoteRepos = config.getRemoteRepositories(); 247 for (RemoteRepositoryConfiguration repo : remoteRepos) { 248 // [MRM-582] Remote Repositories with empty <username> and <password> fields shouldn't be created in configuration. 249 if (StringUtils.isBlank(repo.getUsername())) { 250 repo.setUsername(null); 251 } 252 253 if (StringUtils.isBlank(repo.getPassword())) { 254 repo.setPassword(null); 255 } 256 } 257 } 258 259 if (!config.getProxyConnectors().isEmpty()) { 260 // Fix Proxy Connector Settings. 261 262 // Create a copy of the list to read from (to prevent concurrent modification exceptions) 263 List<ProxyConnectorConfiguration> proxyConnectorList = new ArrayList<>(config.getProxyConnectors()); 264 // Remove the old connector list. 265 config.getProxyConnectors().clear(); 266 267 for (ProxyConnectorConfiguration connector : proxyConnectorList) { 268 // Fix policies 269 boolean connectorValid = true; 270 271 Map<String, String> policies = new HashMap<>(); 272 // Make copy of policies 273 policies.putAll(connector.getPolicies()); 274 // Clear out policies 275 connector.getPolicies().clear(); 276 277 // Work thru policies. cleaning them up. 278 for (Entry<String, String> entry : policies.entrySet()) { 279 String policyId = entry.getKey(); 280 String setting = entry.getValue(); 281 282 // Upgrade old policy settings. 283 if ("releases".equals(policyId) || "snapshots".equals(policyId)) { 284 if ("ignored".equals(setting)) { 285 setting = AbstractUpdatePolicy.ALWAYS.getId(); 286 } else if ("disabled".equals(setting)) { 287 setting = AbstractUpdatePolicy.NEVER.getId(); 288 } 289 } else if ("cache-failures".equals(policyId)) { 290 if ("ignored".equals(setting)) { 291 setting = CachedFailuresPolicy.NO.getId(); 292 } else if ("cached".equals(setting)) { 293 setting = CachedFailuresPolicy.YES.getId(); 294 } 295 } else if ("checksum".equals(policyId)) { 296 if ("ignored".equals(setting)) { 297 setting = ChecksumPolicy.IGNORE.getId(); 298 } 299 } 300 301 // Validate existance of policy key. 302 connector.addPolicy(policyId, setting); 303 } 304 305 if (connectorValid) { 306 config.addProxyConnector(connector); 307 } 308 } 309 310 // Normalize the order fields in the proxy connectors. 311 Map<String, java.util.List<ProxyConnectorConfiguration>> proxyConnectorMap = 312 config.getProxyConnectorAsMap(); 313 314 for (List<ProxyConnectorConfiguration> connectors : proxyConnectorMap.values()) { 315 // Sort connectors by order field. 316 Collections.sort(connectors, ProxyConnectorConfigurationOrderComparator.getInstance()); 317 318 // Normalize the order field values. 319 int order = 1; 320 for (ProxyConnectorConfiguration connector : connectors) { 321 connector.setOrder(order++); 322 } 323 } 324 } 325 326 this.defaultLocale = Locale.forLanguageTag(config.getArchivaRuntimeConfiguration().getDefaultLanguage()); 327 this.languagePriorities = Locale.LanguageRange.parse(config.getArchivaRuntimeConfiguration().getLanguageRange()); 328 return config; 329 } 330 331 /* 332 * Updates the checkpath list for repositories. 333 * 334 * We are replacing existing ones and adding new ones. This allows to update the list with new releases. 335 * 336 * We are also updating existing remote repositories, if they exist already. 337 * 338 * This update method should only be called, if the config version changes to avoid overwriting 339 * user repository settings all the time. 340 */ 341 private void updateCheckPathDefaults(Configuration config, Registry defaultConfiguration) { 342 List<RepositoryCheckPath> existingCheckPathList = config.getArchivaDefaultConfiguration().getDefaultCheckPaths(); 343 HashMap<String, RepositoryCheckPath> existingCheckPaths = new HashMap<>(); 344 HashMap<String, RepositoryCheckPath> newCheckPaths = new HashMap<>(); 345 for (RepositoryCheckPath path : config.getArchivaDefaultConfiguration().getDefaultCheckPaths()) { 346 existingCheckPaths.put(path.getUrl(), path); 347 } 348 List defaultCheckPathsSubsets = defaultConfiguration.getSubsetList("archivaDefaultConfiguration.defaultCheckPaths.defaultCheckPath"); 349 for (Iterator i = defaultCheckPathsSubsets.iterator(); i.hasNext(); ) { 350 RepositoryCheckPath v = readRepositoryCheckPath((Registry) i.next()); 351 if (existingCheckPaths.containsKey(v.getUrl())) { 352 existingCheckPathList.remove(existingCheckPaths.get(v.getUrl())); 353 } 354 existingCheckPathList.add(v); 355 newCheckPaths.put(v.getUrl(), v); 356 } 357 // Remote repositories update 358 for (RemoteRepositoryConfiguration remoteRepositoryConfiguration : config.getRemoteRepositories()) { 359 String url = remoteRepositoryConfiguration.getUrl().toLowerCase(); 360 if (newCheckPaths.containsKey(url)) { 361 String currentPath = remoteRepositoryConfiguration.getCheckPath(); 362 String newPath = newCheckPaths.get(url).getPath(); 363 log.info("Updating connection check path for repository {}, from '{}' to '{}'.", remoteRepositoryConfiguration.getId(), 364 currentPath, newPath); 365 remoteRepositoryConfiguration.setCheckPath(newPath); 366 } 367 } 368 } 369 370 private RepositoryCheckPath readRepositoryCheckPath(Registry registry) { 371 RepositoryCheckPath value = new RepositoryCheckPath(); 372 373 String url = registry.getString("url", value.getUrl()); 374 375 value.setUrl(url); 376 String path = registry.getString("path", value.getPath()); 377 value.setPath(path); 378 return value; 379 } 380 381 382 private Registry readDefaultConfiguration() { 383 // if it contains some old configuration, remove it (Archiva 0.9) 384 registry.removeSubset(KEY); 385 386 try { 387 registry.addConfigurationFromResource("org/apache/archiva/configuration/default-archiva.xml", KEY); 388 this.isConfigurationDefaulted = true; 389 } catch (RegistryException e) { 390 throw new ConfigurationRuntimeException( 391 "Fatal error: Unable to find the built-in default configuration and load it into the registry", e); 392 } 393 return registry.getSubset(KEY); 394 } 395 396 /* 397 * Reads the default only configuration into a special prefix. This allows to check for changes 398 * of the default configuration. 399 */ 400 private Registry readDefaultOnlyConfiguration() { 401 registry.removeSubset(KEY_DEFAULT_ONLY); 402 try { 403 registry.addConfigurationFromResource("org/apache/archiva/configuration/default-archiva.xml", KEY_DEFAULT_ONLY); 404 } catch (RegistryException e) { 405 throw new ConfigurationRuntimeException( 406 "Fatal error: Unable to find the built-in default configuration and load it into the registry", e); 407 } 408 return registry.getSubset(KEY_DEFAULT_ONLY); 409 } 410 411 @SuppressWarnings("unchecked") 412 @Override 413 public synchronized void save(Configuration configuration) 414 throws IndeterminateConfigurationException, RegistryException { 415 Registry section = registry.getSection(KEY + ".user"); 416 Registry baseSection = registry.getSection(KEY + ".base"); 417 if (section == null) { 418 section = baseSection; 419 if (section == null) { 420 section = createDefaultConfigurationFile(); 421 } 422 } else if (baseSection != null) { 423 Collection<String> keys = baseSection.getKeys(); 424 boolean foundList = false; 425 for (Iterator<String> i = keys.iterator(); i.hasNext() && !foundList; ) { 426 String key = i.next(); 427 428 // a little aggressive with the repositoryScanning and databaseScanning - should be no need to split 429 // that configuration 430 if (key.startsWith("repositories") // 431 || key.startsWith("proxyConnectors") // 432 || key.startsWith("networkProxies") // 433 || key.startsWith("repositoryScanning") // 434 || key.startsWith("remoteRepositories") // 435 || key.startsWith("managedRepositories") // 436 || key.startsWith("repositoryGroups")) // 437 { 438 foundList = true; 439 } 440 } 441 442 if (foundList) { 443 this.configuration = null; 444 445 throw new IndeterminateConfigurationException( 446 "Configuration can not be saved when it is loaded from two sources"); 447 } 448 } 449 450 // escape all cron expressions to handle ',' 451 escapeCronExpressions(configuration); 452 453 // [MRM-661] Due to a bug in the modello registry writer, we need to take these out by hand. They'll be put back by the writer. 454 if (section != null) { 455 if (configuration.getManagedRepositories().isEmpty()) { 456 section.removeSubset("managedRepositories"); 457 } 458 if (configuration.getRemoteRepositories().isEmpty()) { 459 section.removeSubset("remoteRepositories"); 460 461 } 462 if (configuration.getProxyConnectors().isEmpty()) { 463 section.removeSubset("proxyConnectors"); 464 } 465 if (configuration.getNetworkProxies().isEmpty()) { 466 section.removeSubset("networkProxies"); 467 } 468 if (configuration.getLegacyArtifactPaths().isEmpty()) { 469 section.removeSubset("legacyArtifactPaths"); 470 } 471 if (configuration.getRepositoryGroups().isEmpty()) { 472 section.removeSubset("repositoryGroups"); 473 } 474 if (configuration.getRepositoryScanning() != null) { 475 if (configuration.getRepositoryScanning().getKnownContentConsumers().isEmpty()) { 476 section.removeSubset("repositoryScanning.knownContentConsumers"); 477 } 478 if (configuration.getRepositoryScanning().getInvalidContentConsumers().isEmpty()) { 479 section.removeSubset("repositoryScanning.invalidContentConsumers"); 480 } 481 } 482 if (configuration.getArchivaRuntimeConfiguration() != null) { 483 section.removeSubset("archivaRuntimeConfiguration.defaultCheckPaths"); 484 } 485 486 new ConfigurationRegistryWriter().write(configuration, section); 487 section.save(); 488 } 489 490 491 this.configuration = unescapeExpressions(configuration); 492 isConfigurationDefaulted = false; 493 494 triggerEvent(ConfigurationEvent.SAVED); 495 } 496 497 private void escapeCronExpressions(Configuration configuration) { 498 for (ManagedRepositoryConfiguration c : configuration.getManagedRepositories()) { 499 c.setRefreshCronExpression(escapeCronExpression(c.getRefreshCronExpression())); 500 } 501 } 502 503 private Registry createDefaultConfigurationFile() 504 throws RegistryException { 505 // TODO: may not be needed under commons-configuration 1.4 - check 506 507 String contents = "<configuration />"; 508 509 String fileLocation = userConfigFilename; 510 511 if (!writeFile("user configuration", userConfigFilename, contents)) { 512 fileLocation = altConfigFilename; 513 if (!writeFile("alternative configuration", altConfigFilename, contents, true)) { 514 throw new RegistryException( 515 "Unable to create configuration file in either user [" + userConfigFilename + "] or alternative [" 516 + altConfigFilename 517 + "] locations on disk, usually happens when not allowed to write to those locations."); 518 } 519 } 520 521 // olamy hackish I know :-) 522 contents = "<configuration><xml fileName=\"" + fileLocation 523 + "\" config-forceCreate=\"true\" config-name=\"org.apache.archiva.user\"/>" + "</configuration>"; 524 525 ((CommonsConfigurationRegistry) registry).setProperties(contents); 526 527 registry.initialize(); 528 529 for (RegistryListener regListener : registryListeners) { 530 addRegistryChangeListener(regListener); 531 } 532 533 triggerEvent(ConfigurationEvent.SAVED); 534 535 Registry section = registry.getSection(KEY + ".user"); 536 return section == null ? new CommonsConfigurationRegistry(new BaseConfiguration()) : section; 537 } 538 539 private boolean writeFile(String filetype, String path, String contents) { 540 return writeFile( filetype, path, contents, false ); 541 } 542 543 /** 544 * Attempts to write the contents to a file, if an IOException occurs, return false. 545 * <p/> 546 * The file will be created if the directory to the file exists, otherwise this will return false. 547 * 548 * @param filetype the filetype (freeform text) to use in logging messages when failure to write. 549 * @param path the path to write to. 550 * @param contents the contents to write. 551 * @return true if write successful. 552 */ 553 private boolean writeFile(String filetype, String path, String contents, boolean createDirs) { 554 Path file = Paths.get(path); 555 556 try { 557 // Check parent directory (if it is declared) 558 final Path parent = file.getParent(); 559 if (parent != null) { 560 // Check that directory exists 561 if (!Files.exists( parent ) && createDirs) { 562 Files.createDirectories( parent ); 563 } 564 if (!Files.isDirectory(parent)) { 565 // Directory to file must exist for file to be created 566 return false; 567 } 568 } 569 FileUtils.writeStringToFile(file.toFile(), contents, FILE_ENCODING); 570 return true; 571 } catch (IOException e) { 572 log.error("Unable to create {} file: {}", filetype, e.getMessage(), e); 573 return false; 574 } 575 } 576 577 private void triggerEvent(int type) { 578 ConfigurationEvent evt = new ConfigurationEvent(type); 579 for (ConfigurationListener listener : listeners) { 580 listener.configurationEvent(evt); 581 } 582 } 583 584 @Override 585 public void addListener(ConfigurationListener listener) { 586 if (listener == null) { 587 return; 588 } 589 590 listeners.add(listener); 591 } 592 593 @Override 594 public void removeListener(ConfigurationListener listener) { 595 if (listener == null) { 596 return; 597 } 598 599 listeners.remove(listener); 600 } 601 602 603 @Override 604 public void addChangeListener(RegistryListener listener) { 605 addRegistryChangeListener(listener); 606 607 // keep track for later 608 registryListeners.add(listener); 609 } 610 611 private void addRegistryChangeListener(RegistryListener listener) { 612 Registry section = registry.getSection(KEY + ".user"); 613 if (section != null) { 614 section.addChangeListener(listener); 615 } 616 section = registry.getSection(KEY + ".base"); 617 if (section != null) { 618 section.addChangeListener(listener); 619 } 620 } 621 622 @Override 623 public void removeChangeListener(RegistryListener listener) { 624 boolean removed = registryListeners.remove(listener); 625 log.debug("RegistryListener: '{}' removed {}", listener, removed); 626 627 Registry section = registry.getSection(KEY + ".user"); 628 if (section != null) { 629 section.removeChangeListener(listener); 630 } 631 section = registry.getSection(KEY + ".base"); 632 if (section != null) { 633 section.removeChangeListener(listener); 634 } 635 636 } 637 638 @PostConstruct 639 public void initialize() { 640 641 // Resolve expressions in the userConfigFilename and altConfigFilename 642 try { 643 ExpressionEvaluator expressionEvaluator = new DefaultExpressionEvaluator(); 644 expressionEvaluator.addExpressionSource(new SystemPropertyExpressionSource()); 645 String userConfigFileNameSysProps = System.getProperty(USER_CONFIG_PROPERTY); 646 if (StringUtils.isNotBlank(userConfigFileNameSysProps)) { 647 userConfigFilename = userConfigFileNameSysProps; 648 } else { 649 String userConfigFileNameEnv = System.getenv(USER_CONFIG_ENVVAR); 650 if (StringUtils.isNotBlank(userConfigFileNameEnv)) { 651 userConfigFilename = userConfigFileNameEnv; 652 } else { 653 userConfigFilename = expressionEvaluator.expand(userConfigFilename); 654 } 655 } 656 altConfigFilename = expressionEvaluator.expand(altConfigFilename); 657 loadConfiguration(); 658 handleUpgradeConfiguration(); 659 } catch (IndeterminateConfigurationException | RegistryException e) { 660 throw new RuntimeException("failed during upgrade from previous version" + e.getMessage(), e); 661 } catch (EvaluatorException e) { 662 throw new RuntimeException( 663 "Unable to evaluate expressions found in " + "userConfigFilename or altConfigFilename.", e); 664 } 665 registry.addChangeListener(this); 666 } 667 668 /** 669 * Handle upgrade to newer version 670 */ 671 private void handleUpgradeConfiguration() 672 throws RegistryException, IndeterminateConfigurationException { 673 674 List<String> dbConsumers = Arrays.asList("update-db-artifact", "update-db-repository-metadata"); 675 676 // remove database consumers if here 677 List<String> intersec = 678 ListUtils.intersection(dbConsumers, configuration.getRepositoryScanning().getKnownContentConsumers()); 679 680 if (!intersec.isEmpty()) { 681 682 List<String> knowContentConsumers = 683 new ArrayList<>(configuration.getRepositoryScanning().getKnownContentConsumers().size()); 684 for (String knowContentConsumer : configuration.getRepositoryScanning().getKnownContentConsumers()) { 685 if (!dbConsumers.contains(knowContentConsumer)) { 686 knowContentConsumers.add(knowContentConsumer); 687 } 688 } 689 690 configuration.getRepositoryScanning().setKnownContentConsumers(knowContentConsumers); 691 } 692 693 // ensure create-archiva-metadata is here 694 if (!configuration.getRepositoryScanning().getKnownContentConsumers().contains("create-archiva-metadata")) { 695 List<String> knowContentConsumers = 696 new ArrayList<>(configuration.getRepositoryScanning().getKnownContentConsumers()); 697 knowContentConsumers.add("create-archiva-metadata"); 698 configuration.getRepositoryScanning().setKnownContentConsumers(knowContentConsumers); 699 } 700 701 // ensure duplicate-artifacts is here 702 if (!configuration.getRepositoryScanning().getKnownContentConsumers().contains("duplicate-artifacts")) { 703 List<String> knowContentConsumers = 704 new ArrayList<>(configuration.getRepositoryScanning().getKnownContentConsumers()); 705 knowContentConsumers.add("duplicate-artifacts"); 706 configuration.getRepositoryScanning().setKnownContentConsumers(knowContentConsumers); 707 } 708 709 Registry defaultOnlyConfiguration = readDefaultOnlyConfiguration(); 710 // Currently we check only for configuration version change, not certain version numbers. 711 if (hasConfigVersionChanged(configuration, defaultOnlyConfiguration)) { 712 updateCheckPathDefaults(configuration, defaultOnlyConfiguration); 713 String newVersion = defaultOnlyConfiguration.getString("version"); 714 if (newVersion == null) { 715 throw new IndeterminateConfigurationException("The default configuration has no version information!"); 716 } 717 configuration.setVersion(newVersion); 718 try { 719 save(configuration); 720 } catch (IndeterminateConfigurationException e) { 721 log.error("Error occured during configuration update to new version: {}", e.getMessage()); 722 } catch (RegistryException e) { 723 log.error("Error occured during configuration update to new version: {}", e.getMessage()); 724 } 725 } 726 } 727 728 @Override 729 public void reload() { 730 this.configuration = null; 731 try { 732 this.registry.initialize(); 733 } catch (RegistryException e) { 734 throw new ConfigurationRuntimeException(e.getMessage(), e); 735 } 736 this.initialize(); 737 } 738 739 @Override 740 public Locale getDefaultLocale() { 741 return defaultLocale; 742 } 743 744 @Override 745 public List<Locale.LanguageRange> getLanguagePriorities() { 746 return languagePriorities; 747 } 748 749 @Override 750 public Path getAppServerBaseDir() { 751 String basePath = registry.getString("appserver.base"); 752 if (!StringUtils.isEmpty(basePath)) { 753 return Paths.get(basePath); 754 } else { 755 return Paths.get(""); 756 } 757 } 758 759 @Override 760 public Path getRepositoryBaseDir() { 761 if (repositoryBaseDirectory == null) { 762 getConfiguration(); 763 } 764 return repositoryBaseDirectory; 765 766 } 767 768 @Override 769 public Path getRemoteRepositoryBaseDir() { 770 if (remoteRepositoryBaseDirectory == null) { 771 getConfiguration(); 772 } 773 return remoteRepositoryBaseDirectory; 774 } 775 776 @Override 777 public Path getRepositoryGroupBaseDir() { 778 if (repositoryGroupBaseDirectory == null) { 779 getConfiguration(); 780 } 781 return repositoryGroupBaseDirectory; 782 } 783 784 @Override 785 public Path getDataDirectory() { 786 if (dataDirectory == null) { 787 getConfiguration(); 788 } 789 return dataDirectory; 790 } 791 792 @Override 793 public void beforeConfigurationChange(Registry registry, String propertyName, Object propertyValue) { 794 // nothing to do here 795 } 796 797 @Override 798 public synchronized void afterConfigurationChange(Registry registry, String propertyName, Object propertyValue) { 799 configuration = null; 800 this.dataDirectory = null; 801 this.repositoryBaseDirectory = null; 802 } 803 804 private String removeExpressions(String directory) { 805 String value = StringUtils.replace(directory, "${appserver.base}", 806 registry.getString("appserver.base", "${appserver.base}")); 807 value = StringUtils.replace(value, "${appserver.home}", 808 registry.getString("appserver.home", "${appserver.home}")); 809 return value; 810 } 811 812 private String unescapeCronExpression(String cronExpression) { 813 return StringUtils.replace(cronExpression, "\\,", ","); 814 } 815 816 private String escapeCronExpression(String cronExpression) { 817 return StringUtils.replace(cronExpression, ",", "\\,"); 818 } 819 820 private Configuration unescapeExpressions(Configuration config) { 821 // TODO: for commons-configuration 1.3 only 822 for (ManagedRepositoryConfiguration c : config.getManagedRepositories()) { 823 c.setLocation(removeExpressions(c.getLocation())); 824 c.setRefreshCronExpression(unescapeCronExpression(c.getRefreshCronExpression())); 825 } 826 827 return config; 828 } 829 830 private Configuration checkRepositoryLocations(Configuration config) { 831 // additional check for [MRM-789], ensure that the location of the default repositories 832 // are not installed in the server installation 833 for (ManagedRepositoryConfiguration repo : (List<ManagedRepositoryConfiguration>) config.getManagedRepositories()) { 834 String repoPath = repo.getLocation(); 835 Path repoLocation = Paths.get(repoPath); 836 837 if (Files.exists(repoLocation) && Files.isDirectory(repoLocation) && !repoPath.endsWith( 838 "data/repositories/" + repo.getId())) { 839 repo.setLocation(repoPath + "/data/repositories/" + repo.getId()); 840 } 841 } 842 843 return config; 844 } 845 846 public String getUserConfigFilename() { 847 return userConfigFilename; 848 } 849 850 public String getAltConfigFilename() { 851 return altConfigFilename; 852 } 853 854 @Override 855 public boolean isDefaulted() { 856 return this.isConfigurationDefaulted; 857 } 858 859 public Registry getRegistry() { 860 return registry; 861 } 862 863 public void setRegistry(Registry registry) { 864 this.registry = registry; 865 } 866 867 868 public void setUserConfigFilename(String userConfigFilename) { 869 this.userConfigFilename = userConfigFilename; 870 } 871 872 public void setAltConfigFilename(String altConfigFilename) { 873 this.altConfigFilename = altConfigFilename; 874 } 875}