]> source.dussan.org Git - archiva.git/blob
44f25212bb9d0e110d5d9d9412827fa171bdab0f
[archiva.git] /
1 package org.apache.archiva.configuration;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.configuration.functors.ProxyConnectorConfigurationOrderComparator;
23 import org.apache.archiva.configuration.io.registry.ConfigurationRegistryReader;
24 import org.apache.archiva.configuration.io.registry.ConfigurationRegistryWriter;
25 import org.apache.archiva.policies.AbstractUpdatePolicy;
26 import org.apache.archiva.policies.CachedFailuresPolicy;
27 import org.apache.archiva.policies.ChecksumPolicy;
28 import org.apache.archiva.policies.DownloadErrorPolicy;
29 import org.apache.archiva.policies.Policy;
30 import org.apache.archiva.policies.PostDownloadPolicy;
31 import org.apache.archiva.policies.PreDownloadPolicy;
32 import org.apache.archiva.redback.components.evaluator.DefaultExpressionEvaluator;
33 import org.apache.archiva.redback.components.evaluator.EvaluatorException;
34 import org.apache.archiva.redback.components.evaluator.ExpressionEvaluator;
35 import org.apache.archiva.redback.components.evaluator.sources.SystemPropertyExpressionSource;
36 import org.apache.archiva.redback.components.registry.Registry;
37 import org.apache.archiva.redback.components.registry.RegistryException;
38 import org.apache.archiva.redback.components.registry.RegistryListener;
39 import org.apache.archiva.redback.components.registry.commons.CommonsConfigurationRegistry;
40 import org.apache.archiva.redback.components.springutils.ComponentContainer;
41 import org.apache.commons.collections.CollectionUtils;
42 import org.apache.commons.collections.ListUtils;
43 import org.apache.commons.collections.MapUtils;
44 import org.apache.commons.configuration.BaseConfiguration;
45 import org.apache.commons.io.FileUtils;
46 import org.apache.commons.lang.StringUtils;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.stereotype.Service;
50
51 import javax.annotation.PostConstruct;
52 import javax.inject.Inject;
53 import javax.inject.Named;
54 import java.io.File;
55 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.HashMap;
61 import java.util.HashSet;
62 import java.util.Iterator;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Map.Entry;
66 import java.util.Set;
67
68 /**
69  * <p>
70  * Implementation of configuration holder that retrieves it from the registry.
71  * </p>
72  * <p>
73  * The registry layers and merges the 2 configuration files: user, and application server.
74  * </p>
75  * <p>
76  * Instead of relying on the model defaults, if the registry is empty a default configuration file is loaded and
77  * applied from a resource. The defaults are not loaded into the registry as the lists (eg repositories) could no longer
78  * be removed if that was the case.
79  * </p>
80  * <p>
81  * When saving the configuration, it is saved to the location it was read from. If it was read from the defaults, it
82  * will be saved to the user location.
83  * However, if the configuration contains information from both sources, an exception is raised as this is currently
84  * unsupported. The reason for this is that it is not possible to identify where to re-save elements, and can result
85  * in list configurations (eg repositories) becoming inconsistent.
86  * </p>
87  * <p>
88  * If the configuration is outdated, it will be upgraded when it is loaded. This is done by checking the version flag
89  * before reading it from the registry.
90  * </p>
91  */
92 @Service("archivaConfiguration#default")
93 public class DefaultArchivaConfiguration
94     implements ArchivaConfiguration, RegistryListener
95 {
96     private Logger log = LoggerFactory.getLogger( DefaultArchivaConfiguration.class );
97
98     /**
99      * 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 }