]> source.dussan.org Git - archiva.git/blob
afa12c4fd9fae359b618966e4c04464ef21d55e8
[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.Policy;
29 import org.apache.archiva.policies.PostDownloadPolicy;
30 import org.apache.archiva.policies.PreDownloadPolicy;
31 import org.apache.commons.collections.CollectionUtils;
32 import org.apache.commons.collections.MapUtils;
33 import org.apache.commons.configuration.BaseConfiguration;
34 import org.apache.commons.io.FileUtils;
35 import org.apache.commons.lang.StringUtils;
36 import org.codehaus.plexus.evaluator.DefaultExpressionEvaluator;
37 import org.codehaus.plexus.evaluator.EvaluatorException;
38 import org.codehaus.plexus.evaluator.ExpressionEvaluator;
39 import org.codehaus.plexus.evaluator.sources.SystemPropertyExpressionSource;
40 import org.codehaus.plexus.registry.Registry;
41 import org.codehaus.plexus.registry.RegistryException;
42 import org.codehaus.plexus.registry.RegistryListener;
43 import org.codehaus.redback.components.registry.commons.CommonsConfigurationRegistry;
44 import org.codehaus.redback.components.springutils.ComponentContainer;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.springframework.stereotype.Service;
48
49 import javax.annotation.PostConstruct;
50 import javax.inject.Inject;
51 import javax.inject.Named;
52 import java.io.File;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collection;
57 import java.util.Collections;
58 import java.util.HashMap;
59 import java.util.HashSet;
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Map.Entry;
64 import java.util.Set;
65
66 /**
67  * <p>
68  * Implementation of configuration holder that retrieves it from the registry.
69  * </p>
70  * <p>
71  * The registry layers and merges the 2 configuration files: user, and application server.
72  * </p>
73  * <p>
74  * Instead of relying on the model defaults, if the registry is empty a default configuration file is loaded and
75  * applied from a resource. The defaults are not loaded into the registry as the lists (eg repositories) could no longer
76  * be removed if that was the case.
77  * </p>
78  * <p>
79  * When saving the configuration, it is saved to the location it was read from. If it was read from the defaults, it
80  * will be saved to the user location.
81  * However, if the configuration contains information from both sources, an exception is raised as this is currently
82  * unsupported. The reason for this is that it is not possible to identify where to re-save elements, and can result
83  * in list configurations (eg repositories) becoming inconsistent.
84  * </p>
85  * <p>
86  * If the configuration is outdated, it will be upgraded when it is loaded. This is done by checking the version flag
87  * before reading it from the registry.
88  * </p>
89  */
90 @Service( "archivaConfiguration#default" )
91 public class DefaultArchivaConfiguration
92     implements ArchivaConfiguration, RegistryListener
93 {
94     private Logger log = LoggerFactory.getLogger( DefaultArchivaConfiguration.class );
95
96     /**
97      * Plexus registry to read the configuration from.
98      */
99     @Inject
100     @Named( value = "commons-configuration" )
101     private Registry registry;
102
103     @Inject
104     private ComponentContainer componentContainer;
105
106     /**
107      * The configuration that has been converted.
108      */
109     private Configuration configuration;
110
111     /**
112      * see #initialize
113      *
114      * @todo these don't strictly belong in here
115      */
116     private Map<String, PreDownloadPolicy> prePolicies;
117
118     /**
119      * see #initialize
120      *
121      * @todo these don't strictly belong in here
122      */
123     private Map<String, PostDownloadPolicy> postPolicies;
124
125     /**
126      * see #initialize
127      * default-value="${user.home}/.m2/archiva.xml"
128      */
129     private String userConfigFilename = "${user.home}/.m2/archiva.xml";
130
131     /**
132      * see #initialize
133      * default-value="${appserver.base}/conf/archiva.xml"
134      */
135     private String altConfigFilename = "${appserver.base}/conf/archiva.xml";
136
137     /**
138      * Configuration Listeners we've registered.
139      */
140     private Set<ConfigurationListener> listeners = new HashSet<ConfigurationListener>( );
141
142     /**
143      * Registry Listeners we've registered.
144      */
145     private Set<RegistryListener> registryListeners = new HashSet<RegistryListener>( );
146
147     /**
148      * Boolean to help determine if the configuration exists as a result of pulling in
149      * the default-archiva.xml
150      */
151     private boolean isConfigurationDefaulted = false;
152
153     private static final String KEY = "org.apache.archiva";
154
155     public Configuration getConfiguration( )
156     {
157         return loadConfiguration( );
158     }
159
160     private synchronized Configuration loadConfiguration( )
161     {
162         if ( configuration == null )
163         {
164             configuration = load( );
165             configuration = unescapeExpressions( configuration );
166             if ( isConfigurationDefaulted )
167             {
168                 configuration = checkRepositoryLocations( configuration );
169             }
170         }
171
172         return configuration;
173     }
174
175     @SuppressWarnings( "unchecked" )
176     private Configuration load( )
177     {
178         // TODO: should this be the same as section? make sure unnamed sections still work (eg, sys properties)
179         Registry subset = registry.getSubset( KEY );
180         if ( subset.getString( "version" ) == null )
181         {
182             // a little autodetection of v1, even if version is omitted (this was previously allowed)
183             if ( subset.getSubset( "repositoryScanning" ).isEmpty( ) )
184             {
185                 // only for empty, or v < 1
186                 subset = readDefaultConfiguration( );
187             }
188         }
189
190         Configuration config = new ConfigurationRegistryReader( ).read( subset );
191
192         config.getRepositoryGroups( );
193         config.getRepositoryGroupsAsMap( );
194         if ( !config.getRepositories( ).isEmpty( ) )
195         {
196             for ( Iterator<V1RepositoryConfiguration> i = config.getRepositories( ).iterator( ); i.hasNext( ); )
197             {
198                 V1RepositoryConfiguration r = i.next( );
199                 r.setScanned( r.isIndexed( ) );
200
201                 if ( StringUtils.startsWith( r.getUrl( ), "file://" ) )
202                 {
203                     r.setLocation( r.getUrl( ).substring( 7 ) );
204                     config.addManagedRepository( r );
205                 }
206                 else if ( StringUtils.startsWith( r.getUrl( ), "file:" ) )
207                 {
208                     r.setLocation( r.getUrl( ).substring( 5 ) );
209                     config.addManagedRepository( r );
210                 }
211                 else if ( StringUtils.isEmpty( r.getUrl( ) ) )
212                 {
213                     // in case of empty url we can consider it as a managed one
214                     // check if location is null
215                     //file://${appserver.base}/repositories/${id}
216                     if ( StringUtils.isEmpty( r.getLocation( ) ) )
217                     {
218                         r.setLocation( "file://${appserver.base}/repositories/" + r.getId( ) );
219                     }
220                     config.addManagedRepository( r );
221                 }
222                 else
223                 {
224                     RemoteRepositoryConfiguration repo = new RemoteRepositoryConfiguration( );
225                     repo.setId( r.getId( ) );
226                     repo.setLayout( r.getLayout( ) );
227                     repo.setName( r.getName( ) );
228                     repo.setUrl( r.getUrl( ) );
229                     config.addRemoteRepository( repo );
230                 }
231             }
232
233             // Prevent duplicate repositories from showing up.
234             config.getRepositories( ).clear( );
235
236             registry.removeSubset( KEY + ".repositories" );
237         }
238
239         if ( !CollectionUtils.isEmpty( config.getRemoteRepositories( ) ) )
240         {
241             List<RemoteRepositoryConfiguration> remoteRepos = config.getRemoteRepositories( );
242             for ( RemoteRepositoryConfiguration repo : remoteRepos )
243             {
244                 // [MRM-582] Remote Repositories with empty <username> and <password> fields shouldn't be created in configuration.
245                 if ( StringUtils.isBlank( repo.getUsername( ) ) )
246                 {
247                     repo.setUsername( null );
248                 }
249
250                 if ( StringUtils.isBlank( repo.getPassword( ) ) )
251                 {
252                     repo.setPassword( null );
253                 }
254             }
255         }
256
257         if ( !config.getProxyConnectors( ).isEmpty( ) )
258         {
259             // Fix Proxy Connector Settings.
260
261             List<ProxyConnectorConfiguration> proxyConnectorList = new ArrayList<ProxyConnectorConfiguration>( );
262             // Create a copy of the list to read from (to prevent concurrent modification exceptions)
263             proxyConnectorList.addAll( config.getProxyConnectors( ) );
264             // Remove the old connector list.
265             config.getProxyConnectors( ).clear( );
266
267             for ( ProxyConnectorConfiguration connector : proxyConnectorList )
268             {
269                 // Fix policies
270                 boolean connectorValid = true;
271
272                 Map<String, String> policies = new HashMap<String, String>( );
273                 // Make copy of policies
274                 policies.putAll( connector.getPolicies( ) );
275                 // Clear out policies
276                 connector.getPolicies( ).clear( );
277
278                 // Work thru policies. cleaning them up.
279                 for ( Entry<String, String> entry : policies.entrySet( ) )
280                 {
281                     String policyId = entry.getKey( );
282                     String setting = entry.getValue( );
283
284                     // Upgrade old policy settings.
285                     if ( "releases".equals( policyId ) || "snapshots".equals( policyId ) )
286                     {
287                         if ( "ignored".equals( setting ) )
288                         {
289                             setting = AbstractUpdatePolicy.ALWAYS;
290                         }
291                         else if ( "disabled".equals( setting ) )
292                         {
293                             setting = AbstractUpdatePolicy.NEVER;
294                         }
295                     }
296                     else if ( "cache-failures".equals( policyId ) )
297                     {
298                         if ( "ignored".equals( setting ) )
299                         {
300                             setting = CachedFailuresPolicy.NO;
301                         }
302                         else if ( "cached".equals( setting ) )
303                         {
304                             setting = CachedFailuresPolicy.YES;
305                         }
306                     }
307                     else if ( "checksum".equals( policyId ) )
308                     {
309                         if ( "ignored".equals( setting ) )
310                         {
311                             setting = ChecksumPolicy.IGNORE;
312                         }
313                     }
314
315                     // Validate existance of policy key.
316                     if ( policyExists( policyId ) )
317                     {
318                         Policy policy = findPolicy( policyId );
319                         // Does option exist?
320                         if ( !policy.getOptions( ).contains( setting ) )
321                         {
322                             setting = policy.getDefaultOption( );
323                         }
324                         connector.addPolicy( policyId, setting );
325                     }
326                     else
327                     {
328                         // Policy key doesn't exist. Don't add it to golden version.
329                         log.warn( "Policy [" + policyId + "] does not exist." );
330                     }
331                 }
332
333                 if ( connectorValid )
334                 {
335                     config.addProxyConnector( connector );
336                 }
337             }
338
339             // Normalize the order fields in the proxy connectors.
340             Map<String, java.util.List<ProxyConnectorConfiguration>> proxyConnectorMap =
341                 config.getProxyConnectorAsMap( );
342
343             for ( List<ProxyConnectorConfiguration> connectors : proxyConnectorMap.values( ) )
344             {
345                 // Sort connectors by order field.
346                 Collections.sort( connectors, ProxyConnectorConfigurationOrderComparator.getInstance( ) );
347
348                 // Normalize the order field values.
349                 int order = 1;
350                 for ( ProxyConnectorConfiguration connector : connectors )
351                 {
352                     connector.setOrder( order++ );
353                 }
354             }
355         }
356
357         return config;
358     }
359
360     private Policy findPolicy( String policyId )
361     {
362         if ( MapUtils.isEmpty( prePolicies ) )
363         {
364             log.error( "No PreDownloadPolicies found!" );
365             return null;
366         }
367
368         if ( MapUtils.isEmpty( postPolicies ) )
369         {
370             log.error( "No PostDownloadPolicies found!" );
371             return null;
372         }
373
374         Policy policy;
375
376         policy = prePolicies.get( policyId );
377         if ( policy != null )
378         {
379             return policy;
380         }
381
382         policy = postPolicies.get( policyId );
383         if ( policy != null )
384         {
385             return policy;
386         }
387
388         return null;
389     }
390
391     private boolean policyExists( String policyId )
392     {
393         if ( MapUtils.isEmpty( prePolicies ) )
394         {
395             log.error( "No PreDownloadPolicies found!" );
396             return false;
397         }
398
399         if ( MapUtils.isEmpty( postPolicies ) )
400         {
401             log.error( "No PostDownloadPolicies found!" );
402             return false;
403         }
404
405         return ( prePolicies.containsKey( policyId ) || postPolicies.containsKey( policyId ) );
406     }
407
408     private Registry readDefaultConfiguration( )
409     {
410         // if it contains some old configuration, remove it (Archiva 0.9)
411         registry.removeSubset( KEY );
412
413         try
414         {
415             registry.addConfigurationFromResource( "org/apache/archiva/configuration/default-archiva.xml", KEY );
416             this.isConfigurationDefaulted = true;
417         }
418         catch ( RegistryException e )
419         {
420             throw new ConfigurationRuntimeException(
421                 "Fatal error: Unable to find the built-in default configuration and load it into the registry", e );
422         }
423         return registry.getSubset( KEY );
424     }
425
426     @SuppressWarnings( "unchecked" )
427     public synchronized void save( Configuration configuration )
428         throws IndeterminateConfigurationException, RegistryException
429     {
430         Registry section = registry.getSection( KEY + ".user" );
431         Registry baseSection = registry.getSection( KEY + ".base" );
432         if ( section == null )
433         {
434             section = baseSection;
435             if ( section == null )
436             {
437                 section = createDefaultConfigurationFile( );
438             }
439         }
440         else if ( baseSection != null )
441         {
442             Collection<String> keys = baseSection.getKeys( );
443             boolean foundList = false;
444             for ( Iterator<String> i = keys.iterator( ); i.hasNext( ) && !foundList; )
445             {
446                 String key = i.next( );
447
448                 // a little aggressive with the repositoryScanning and databaseScanning - should be no need to split
449                 // that configuration
450                 if ( key.startsWith( "repositories" ) || key.startsWith( "proxyConnectors" ) || key.startsWith(
451                     "networkProxies" ) || key.startsWith( "repositoryScanning" ) || key.startsWith(
452                     "remoteRepositories" ) || key.startsWith( "managedRepositories" ) || key.startsWith(
453                     "repositoryGroups" ) )
454                 {
455                     foundList = true;
456                 }
457             }
458
459             if ( foundList )
460             {
461                 this.configuration = null;
462
463                 throw new IndeterminateConfigurationException(
464                     "Configuration can not be saved when it is loaded from two sources" );
465             }
466         }
467
468         // escape all cron expressions to handle ','
469         escapeCronExpressions( configuration );
470
471         // [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.
472         if ( configuration.getManagedRepositories( ).isEmpty( ) && section != null )
473         {
474             section.removeSubset( "managedRepositories" );
475         }
476         if ( configuration.getRemoteRepositories( ).isEmpty( ) && section != null )
477         {
478             section.removeSubset( "remoteRepositories" );
479
480         }
481         if ( configuration.getProxyConnectors( ).isEmpty( ) && section != null )
482         {
483             section.removeSubset( "proxyConnectors" );
484         }
485         if ( configuration.getNetworkProxies( ).isEmpty( ) && section != null )
486         {
487             section.removeSubset( "networkProxies" );
488         }
489         if ( configuration.getLegacyArtifactPaths( ).isEmpty( ) && section != null )
490         {
491             section.removeSubset( "legacyArtifactPaths" );
492         }
493         if ( configuration.getRepositoryGroups( ).isEmpty( ) && section != null )
494         {
495             section.removeSubset( "repositoryGroups" );
496         }
497         if ( configuration.getRepositoryScanning( ) != null )
498         {
499             if ( configuration.getRepositoryScanning( ).getKnownContentConsumers( ).isEmpty( ) && section != null )
500             {
501                 section.removeSubset( "repositoryScanning.knownContentConsumers" );
502             }
503             if ( configuration.getRepositoryScanning( ).getInvalidContentConsumers( ).isEmpty( ) && section != null )
504             {
505                 section.removeSubset( "repositoryScanning.invalidContentConsumers" );
506             }
507         }
508
509         new ConfigurationRegistryWriter( ).write( configuration, section );
510         section.save( );
511
512         this.configuration = unescapeExpressions( configuration );
513
514         triggerEvent( ConfigurationEvent.SAVED );
515     }
516
517     private void escapeCronExpressions( Configuration configuration )
518     {
519         for ( ManagedRepositoryConfiguration c : (List<ManagedRepositoryConfiguration>) configuration.getManagedRepositories( ) )
520         {
521             c.setRefreshCronExpression( escapeCronExpression( c.getRefreshCronExpression( ) ) );
522         }
523
524
525     }
526
527     private Registry createDefaultConfigurationFile( )
528         throws RegistryException
529     {
530         // TODO: may not be needed under commons-configuration 1.4 - check
531         // UPDATE: Upgrading to commons-configuration 1.4 breaks half the unit tests. 2007-10-11 (joakime)
532
533         String contents = "<configuration />";
534
535         String fileLocation = userConfigFilename;
536
537         if ( !writeFile( "user configuration", userConfigFilename, contents ) )
538         {
539             fileLocation = altConfigFilename;
540             if ( !writeFile( "alternative configuration", altConfigFilename, contents ) )
541             {
542                 throw new RegistryException(
543                     "Unable to create configuration file in either user [" + userConfigFilename + "] or alternative ["
544                         + altConfigFilename
545                         + "] locations on disk, usually happens when not allowed to write to those locations." );
546             }
547         }
548
549         // olamy hackish I know :-)
550         contents = "<configuration><xml fileName=\"" + fileLocation
551             + "\" config-forceCreate=\"true\" config-name=\"org.apache.archiva.user\"/>" + "</configuration>";
552
553         ( (CommonsConfigurationRegistry) registry ).setProperties( contents );
554
555         registry.initialize( );
556
557         for ( RegistryListener regListener : registryListeners )
558         {
559             addRegistryChangeListener( regListener );
560         }
561
562         triggerEvent( ConfigurationEvent.SAVED );
563
564         Registry section = registry.getSection( KEY + ".user" );
565         return section == null ? new CommonsConfigurationRegistry( new BaseConfiguration( ) ) : section;
566     }
567
568     /**
569      * Attempts to write the contents to a file, if an IOException occurs, return false.
570      * <p/>
571      * The file will be created if the directory to the file exists, otherwise this will return false.
572      *
573      * @param filetype the filetype (freeform text) to use in logging messages when failure to write.
574      * @param path     the path to write to.
575      * @param contents the contents to write.
576      * @return true if write successful.
577      */
578     private boolean writeFile( String filetype, String path, String contents )
579     {
580         File file = new File( path );
581
582         try
583         {
584             // Check parent directory (if it is declared)
585             if ( file.getParentFile( ) != null )
586             {
587                 // Check that directory exists
588                 if ( ( file.getParentFile( ).exists( ) == false ) || ( file.getParentFile( ).isDirectory( ) == false ) )
589                 {
590                     // Directory to file must exist for file to be created
591                     return false;
592                 }
593             }
594
595             FileUtils.writeStringToFile( file, contents, "UTF-8" );
596             return true;
597         }
598         catch ( IOException e )
599         {
600             log.error( "Unable to create " + filetype + " file: " + e.getMessage( ), e );
601             return false;
602         }
603     }
604
605     private void triggerEvent( int type )
606     {
607         ConfigurationEvent evt = new ConfigurationEvent( type );
608         for ( ConfigurationListener listener : listeners )
609         {
610             listener.configurationEvent( evt );
611         }
612     }
613
614     public void addListener( ConfigurationListener listener )
615     {
616         if ( listener == null )
617         {
618             return;
619         }
620
621         listeners.add( listener );
622     }
623
624     public void removeListener( ConfigurationListener listener )
625     {
626         if ( listener == null )
627         {
628             return;
629         }
630
631         listeners.remove( listener );
632     }
633
634     public void addChangeListener( RegistryListener listener )
635     {
636         addRegistryChangeListener( listener );
637
638         // keep track for later
639         registryListeners.add( listener );
640     }
641
642     private void addRegistryChangeListener( RegistryListener listener )
643     {
644         Registry section = registry.getSection( KEY + ".user" );
645         if ( section != null )
646         {
647             section.addChangeListener( listener );
648         }
649         section = registry.getSection( KEY + ".base" );
650         if ( section != null )
651         {
652             section.addChangeListener( listener );
653         }
654     }
655
656     @PostConstruct
657     public void initialize( )
658     {
659
660         this.postPolicies = componentContainer.buildMapWithRole( PostDownloadPolicy.class );
661         this.prePolicies = componentContainer.buildMapWithRole( PreDownloadPolicy.class );
662         // Resolve expressions in the userConfigFilename and altConfigFilename
663         try
664         {
665             ExpressionEvaluator expressionEvaluator = new DefaultExpressionEvaluator( );
666             expressionEvaluator.addExpressionSource( new SystemPropertyExpressionSource( ) );
667             String userConfigFileNameSysProps = System.getProperty( "archiva.user.configFileName" );
668             if ( StringUtils.isNotBlank( userConfigFileNameSysProps ) )
669             {
670                 userConfigFilename = userConfigFileNameSysProps;
671             }
672             else
673             {
674                 userConfigFilename = expressionEvaluator.expand( userConfigFilename );
675             }
676             altConfigFilename = expressionEvaluator.expand( altConfigFilename );
677             loadConfiguration( );
678             handleUpgradeConfiguration( );
679         }
680         catch ( IndeterminateConfigurationException e )
681         {
682             throw new RuntimeException( "failed during upgrade from previous version" + e.getMessage( ), e );
683         }
684         catch ( RegistryException e )
685         {
686             throw new RuntimeException( "failed during upgrade from previous version" + e.getMessage( ), e );
687         }
688         catch ( EvaluatorException e )
689         {
690             throw new RuntimeException(
691                 "Unable to evaluate expressions found in " + "userConfigFilename or altConfigFilename." );
692         }
693         registry.addChangeListener( this );
694     }
695
696     /**
697      * upgrade from 1.3
698      */
699     private void handleUpgradeConfiguration( )
700         throws RegistryException, IndeterminateConfigurationException
701     {
702
703         // remove database consumers
704         List<String> dbConsumers = Arrays.asList( "update-db-artifact", "update-db-repository-metadata" );
705         List<String> knowContentConsumers = new ArrayList<String>( );
706         for ( String knowContentConsumer : configuration.getRepositoryScanning( ).getKnownContentConsumers( ) )
707         {
708             if ( !dbConsumers.contains( knowContentConsumer ) )
709             {
710                 knowContentConsumers.add( knowContentConsumer );
711             }
712         }
713
714         configuration.getRepositoryScanning( ).setKnownContentConsumers( knowContentConsumers );
715         save( configuration );
716
717         // ensure create-archiva-metadata is here
718         if ( !configuration.getRepositoryScanning( ).getKnownContentConsumers( ).contains( "create-archiva-metadata" ) )
719         {
720             knowContentConsumers =
721                 new ArrayList<String>( configuration.getRepositoryScanning( ).getKnownContentConsumers( ) );
722             knowContentConsumers.add( "create-archiva-metadata" );
723             configuration.getRepositoryScanning( ).setKnownContentConsumers( knowContentConsumers );
724             save( configuration );
725         }
726
727         // ensure duplicate-artifacts is here
728         if ( !configuration.getRepositoryScanning( ).getKnownContentConsumers( ).contains( "duplicate-artifacts" ) )
729         {
730             knowContentConsumers =
731                 new ArrayList<String>( configuration.getRepositoryScanning( ).getKnownContentConsumers( ) );
732             knowContentConsumers.add( "duplicate-artifacts" );
733             configuration.getRepositoryScanning( ).setKnownContentConsumers( knowContentConsumers );
734             save( configuration );
735         }
736     }
737
738     public void reload( )
739     {
740         this.configuration = null;
741         try
742         {
743             this.registry.initialize( );
744         }
745         catch ( RegistryException e )
746         {
747             throw new ConfigurationRuntimeException( e.getMessage( ), e );
748         }
749         this.initialize( );
750     }
751
752     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
753     {
754         // nothing to do here
755     }
756
757     public synchronized void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
758     {
759         configuration = null;
760     }
761
762     private String removeExpressions( String directory )
763     {
764         String value = StringUtils.replace( directory, "${appserver.base}",
765                                             registry.getString( "appserver.base", "${appserver.base}" ) );
766         value = StringUtils.replace( value, "${appserver.home}",
767                                      registry.getString( "appserver.home", "${appserver.home}" ) );
768         return value;
769     }
770
771     private String unescapeCronExpression( String cronExpression )
772     {
773         return StringUtils.replace( cronExpression, "\\,", "," );
774     }
775
776     private String escapeCronExpression( String cronExpression )
777     {
778         return StringUtils.replace( cronExpression, ",", "\\," );
779     }
780
781     private Configuration unescapeExpressions( Configuration config )
782     {
783         // TODO: for commons-configuration 1.3 only
784         for ( Iterator<ManagedRepositoryConfiguration> i = config.getManagedRepositories( ).iterator( ); i.hasNext( ); )
785         {
786             ManagedRepositoryConfiguration c = i.next( );
787             c.setLocation( removeExpressions( c.getLocation( ) ) );
788             c.setRefreshCronExpression( unescapeCronExpression( c.getRefreshCronExpression( ) ) );
789         }
790
791         return config;
792     }
793
794     private Configuration checkRepositoryLocations( Configuration config )
795     {
796         // additional check for [MRM-789], ensure that the location of the default repositories 
797         // are not installed in the server installation        
798         for ( ManagedRepositoryConfiguration repo : (List<ManagedRepositoryConfiguration>) config.getManagedRepositories( ) )
799         {
800             String repoPath = repo.getLocation( );
801             File repoLocation = new File( repoPath );
802
803             if ( repoLocation.exists( ) && repoLocation.isDirectory( ) && !repoPath.endsWith(
804                 "data/repositories/" + repo.getId( ) ) )
805             {
806                 repo.setLocation( repoPath + "/data/repositories/" + repo.getId( ) );
807             }
808         }
809
810         return config;
811     }
812
813     public String getUserConfigFilename( )
814     {
815         return userConfigFilename;
816     }
817
818     public String getAltConfigFilename( )
819     {
820         return altConfigFilename;
821     }
822
823     public boolean isDefaulted( )
824     {
825         return this.isConfigurationDefaulted;
826     }
827
828     public Registry getRegistry( )
829     {
830         return registry;
831     }
832
833     public void setRegistry( Registry registry )
834     {
835         this.registry = registry;
836     }
837
838
839     public void setUserConfigFilename( String userConfigFilename )
840     {
841         this.userConfigFilename = userConfigFilename;
842     }
843
844     public void setAltConfigFilename( String altConfigFilename )
845     {
846         this.altConfigFilename = altConfigFilename;
847     }
848 }