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