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