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 36KB

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