1 package org.apache.maven.archiva.configuration;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import org.apache.commons.io.FileUtils;
23 import org.apache.maven.archiva.configuration.functors.ProxyConnectorConfigurationOrderComparator;
24 import org.apache.maven.archiva.configuration.io.registry.ConfigurationRegistryReader;
25 import org.apache.maven.archiva.configuration.io.registry.ConfigurationRegistryWriter;
26 import org.codehaus.plexus.evaluator.DefaultExpressionEvaluator;
27 import org.codehaus.plexus.evaluator.EvaluatorException;
28 import org.codehaus.plexus.evaluator.ExpressionEvaluator;
29 import org.codehaus.plexus.evaluator.sources.SystemPropertyExpressionSource;
30 import org.codehaus.plexus.logging.AbstractLogEnabled;
31 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
32 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
33 import org.codehaus.plexus.registry.Registry;
34 import org.codehaus.plexus.registry.RegistryException;
35 import org.codehaus.plexus.registry.RegistryListener;
36 import org.codehaus.plexus.util.StringUtils;
39 import java.io.IOException;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.HashSet;
43 import java.util.Iterator;
44 import java.util.List;
50 * Implementation of configuration holder that retrieves it from the registry.
53 * The registry layers and merges the 2 configuration files: user, and application server.
56 * Instead of relying on the model defaults, if the registry is empty a default configuration file is loaded and
57 * applied from a resource. The defaults are not loaded into the registry as the lists (eg repositories) could no longer
58 * be removed if that was the case.
61 * When saving the configuration, it is saved to the location it was read from. If it was read from the defaults, it
62 * will be saved to the user location.
63 * However, if the configuration contains information from both sources, an exception is raised as this is currently
64 * unsupported. The reason for this is that it is not possible to identify where to re-save elements, and can result
65 * in list configurations (eg repositories) becoming inconsistent.
68 * If the configuration is outdated, it will be upgraded when it is loaded. This is done by checking the version flag
69 * before reading it from the registry.
72 * @plexus.component role="org.apache.maven.archiva.configuration.ArchivaConfiguration"
74 public class DefaultArchivaConfiguration
75 extends AbstractLogEnabled
76 implements ArchivaConfiguration, RegistryListener, Initializable
79 * Plexus registry to read the configuration from.
81 * @plexus.requirement role-hint="commons-configuration"
83 private Registry registry;
86 * The configuration that has been converted.
88 private Configuration configuration;
90 private static final String KEY = "org.apache.maven.archiva";
93 * @plexus.configuration default-value="${user.home}/.m2/archiva.xml"
95 private String userConfigFilename;
98 * @plexus.configuration default-value="${appserver.base}/conf/archiva.xml"
100 private String altConfigFilename;
103 * Configuration Listeners we've registered.
105 private Set<ConfigurationListener> listeners = new HashSet<ConfigurationListener>();
108 * Registry Listeners we've registered.
110 private Set<RegistryListener> registryListeners = new HashSet<RegistryListener>();
113 * Boolean to help determine if the configuration exists as a result of pulling in
114 * the default-archiva.xml
116 private boolean isConfigurationDefaulted = false;
118 public synchronized Configuration getConfiguration()
120 if ( configuration == null )
122 configuration = load();
123 configuration = processExpressions( configuration );
126 return configuration;
129 private Configuration load()
131 // TODO: should this be the same as section? make sure unnamed sections still work (eg, sys properties)
132 Registry subset = registry.getSubset( KEY );
133 if ( subset.getString( "version" ) == null )
135 // a little autodetection of v1, even if version is omitted (this was previously allowed)
136 if ( subset.getSubset( "repositoryScanning" ).isEmpty() )
138 // only for empty, or v < 1
139 subset = readDefaultConfiguration();
143 Configuration config = new ConfigurationRegistryReader().read( subset );
145 if ( !config.getRepositories().isEmpty() )
147 for ( Iterator<V1RepositoryConfiguration> i = config.getRepositories().iterator(); i.hasNext(); )
149 V1RepositoryConfiguration r = i.next();
150 r.setScanned( r.isIndexed() );
152 if ( r.getUrl().startsWith( "file://" ) )
154 r.setLocation( r.getUrl().substring( 7 ) );
155 config.addManagedRepository( r );
157 else if ( r.getUrl().startsWith( "file:" ) )
159 r.setLocation( r.getUrl().substring( 5 ) );
160 config.addManagedRepository( r );
164 RemoteRepositoryConfiguration repo = new RemoteRepositoryConfiguration();
165 repo.setId( r.getId() );
166 repo.setLayout( r.getLayout() );
167 repo.setName( r.getName() );
168 repo.setUrl( r.getUrl() );
169 config.addRemoteRepository( repo );
173 // Prevent duplicate repositories from showing up.
174 config.getRepositories().clear();
177 // Normalize the order fields in the proxy connectors.
178 if ( !config.getProxyConnectors().isEmpty() )
180 Map<String, java.util.List<ProxyConnectorConfiguration>> proxyConnectorMap = config
181 .getProxyConnectorAsMap();
183 for ( String key : proxyConnectorMap.keySet() )
185 List<ProxyConnectorConfiguration> connectors = proxyConnectorMap.get( key );
186 // Sort connectors by order field.
187 Collections.sort( connectors, ProxyConnectorConfigurationOrderComparator.getInstance() );
189 // Normalize the order field values.
191 for ( ProxyConnectorConfiguration connector : connectors )
193 connector.setOrder( order++ );
201 private Registry readDefaultConfiguration()
203 // if it contains some old configuration, remove it (Archiva 0.9)
204 registry.removeSubset( KEY );
208 registry.addConfigurationFromResource( "org/apache/maven/archiva/configuration/default-archiva.xml", KEY );
209 this.isConfigurationDefaulted = true;
211 catch ( RegistryException e )
213 throw new ConfigurationRuntimeException(
214 "Fatal error: Unable to find the built-in default configuration and load it into the registry",
217 return registry.getSubset( KEY );
220 public synchronized void save( Configuration configuration )
221 throws RegistryException, IndeterminateConfigurationException
223 Registry section = registry.getSection( KEY + ".user" );
224 Registry baseSection = registry.getSection( KEY + ".base" );
225 if ( section == null )
227 section = baseSection;
228 if ( section == null )
230 section = createDefaultConfigurationFile();
233 else if ( baseSection != null )
235 Collection<String> keys = baseSection.getKeys();
236 boolean foundList = false;
237 for ( Iterator<String> i = keys.iterator(); i.hasNext() && !foundList; )
239 String key = i.next();
241 // a little aggressive with the repositoryScanning and databaseScanning - should be no need to split
242 // that configuration
243 if ( key.startsWith( "repositories" ) || key.startsWith( "proxyConnectors" )
244 || key.startsWith( "networkProxies" ) || key.startsWith( "repositoryScanning" )
245 || key.startsWith( "databaseScanning" ) || key.startsWith( "remoteRepositories" )
246 || key.startsWith( "managedRepositories" ) )
254 this.configuration = null;
256 throw new IndeterminateConfigurationException(
257 "Configuration can not be saved when it is loaded from two sources" );
261 // escape all cron expressions to handle ','
262 for ( Iterator<ManagedRepositoryConfiguration> i = configuration.getManagedRepositories().iterator(); i
265 ManagedRepositoryConfiguration c = i.next();
266 c.setRefreshCronExpression( escapeCronExpression( c.getRefreshCronExpression() ) );
269 if ( configuration.getDatabaseScanning() != null )
271 configuration.getDatabaseScanning().setCronExpression(
272 escapeCronExpression( configuration
273 .getDatabaseScanning().getCronExpression() ) );
276 new ConfigurationRegistryWriter().write( configuration, section );
279 triggerEvent( ConfigurationEvent.SAVED );
281 this.configuration = processExpressions( configuration );
284 private Registry createDefaultConfigurationFile()
285 throws RegistryException
287 // TODO: may not be needed under commons-configuration 1.4 - check
288 // UPDATE: Upgrading to commons-configuration 1.4 breaks half the unit tests. 2007-10-11 (joakime)
290 String contents = "<configuration />";
291 if ( !writeFile( "user configuration", userConfigFilename, contents ) )
293 if ( !writeFile( "alternative configuration", altConfigFilename, contents ) )
295 throw new RegistryException( "Unable to create configuration file in either user ["
296 + userConfigFilename + "] or alternative [" + altConfigFilename
297 + "] locations on disk, usually happens when not allowed to write to those locations." );
303 ( (Initializable) registry ).initialize();
305 for ( RegistryListener regListener : registryListeners )
307 addRegistryChangeListener( regListener );
310 catch ( InitializationException e )
312 throw new RegistryException( "Unable to reinitialize configuration: " + e.getMessage(), e );
315 triggerEvent( ConfigurationEvent.SAVED );
317 return registry.getSection( KEY + ".user" );
321 * Attempts to write the contents to a file, if an IOException occurs, return false.
323 * @param filetype the filetype (freeform text) to use in logging messages when failure to write.
324 * @param path the path to write to.
325 * @param contents the contents to write.
326 * @return true if write successful.
328 private boolean writeFile( String filetype, String path, String contents )
330 File file = new File( path );
334 FileUtils.writeStringToFile( file, contents, "UTF-8" );
337 catch ( IOException e )
339 getLogger().error( "Unable to create " + filetype + " file: " + e.getMessage(), e );
344 private void triggerEvent( int type )
346 ConfigurationEvent evt = new ConfigurationEvent( type );
347 for ( ConfigurationListener listener : listeners )
351 listener.configurationEvent( evt );
353 catch ( Throwable t )
355 getLogger().warn( "Unable to notify of saved configuration event.", t );
360 public void addListener( ConfigurationListener listener )
362 if ( listener == null )
367 listeners.add( listener );
370 public void removeListener( ConfigurationListener listener )
372 if ( listener == null )
377 listeners.remove( listener );
380 public void addChangeListener( RegistryListener listener )
382 addRegistryChangeListener( listener );
384 // keep track for later
385 registryListeners.add( listener );
388 private void addRegistryChangeListener( RegistryListener listener )
390 Registry section = registry.getSection( KEY + ".user" );
391 if ( section != null )
393 section.addChangeListener( listener );
395 section = registry.getSection( KEY + ".base" );
396 if ( section != null )
398 section.addChangeListener( listener );
402 public void initialize()
403 throws InitializationException
405 // Resolve expressions in the userConfigFilename and altConfigFilename
408 ExpressionEvaluator expressionEvaluator = new DefaultExpressionEvaluator();
409 expressionEvaluator.addExpressionSource( new SystemPropertyExpressionSource() );
410 userConfigFilename = expressionEvaluator.expand( userConfigFilename );
411 altConfigFilename = expressionEvaluator.expand( altConfigFilename );
413 catch ( EvaluatorException e )
415 throw new InitializationException( "Unable to evaluate expressions found in "
416 + "userConfigFilename or altConfigFilename." );
419 registry.addChangeListener( this );
422 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
424 // nothing to do here
427 public synchronized void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
429 configuration = null;
432 private String removeExpressions( String directory )
434 String value = StringUtils.replace( directory, "${appserver.base}", registry.getString( "appserver.base",
435 "${appserver.base}" ) );
436 value = StringUtils.replace( value, "${appserver.home}", registry.getString( "appserver.home",
437 "${appserver.home}" ) );
441 private String unescapeCronExpression( String cronExpression )
443 return StringUtils.replace( cronExpression, "\\,", "," );
446 private String escapeCronExpression( String cronExpression )
448 return StringUtils.replace( cronExpression, ",", "\\," );
451 private Configuration processExpressions( Configuration config )
453 // TODO: for commons-configuration 1.3 only
454 for ( Iterator<ManagedRepositoryConfiguration> i = config.getManagedRepositories().iterator(); i.hasNext(); )
456 ManagedRepositoryConfiguration c = i.next();
457 c.setLocation( removeExpressions( c.getLocation() ) );
458 c.setRefreshCronExpression( unescapeCronExpression( c.getRefreshCronExpression() ) );
461 DatabaseScanningConfiguration databaseScanning = config.getDatabaseScanning();
462 if ( databaseScanning != null )
464 String cron = databaseScanning.getCronExpression();
465 databaseScanning.setCronExpression( unescapeCronExpression( cron ) );
471 public String getUserConfigFilename()
473 return userConfigFilename;
476 public String getAltConfigFilename()
478 return altConfigFilename;
481 public boolean isDefaulted()
483 return this.isConfigurationDefaulted;