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