You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DefaultArchivaConfiguration.java 38KB

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