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