diff options
author | Martin Stockhammer <martin_s@apache.org> | 2021-06-27 11:35:23 +0200 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2021-06-27 11:35:23 +0200 |
commit | e8a70027d8342f78272b471aa0ac963c0a6f89be (patch) | |
tree | 26ff0e859616e117d65103ebbea11ddf50b43c87 /archiva-modules/archiva-base | |
parent | a1b92c562fae957e601875e50f87a3ff93fecfd6 (diff) | |
download | archiva-e8a70027d8342f78272b471aa0ac963c0a6f89be.tar.gz archiva-e8a70027d8342f78272b471aa0ac963c0a6f89be.zip |
Changing repository group handling
Diffstat (limited to 'archiva-modules/archiva-base')
17 files changed, 1672 insertions, 234 deletions
diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/RepositoryGroupConfiguration.java b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/RepositoryGroupConfiguration.java index cf518951c..d0dc381e4 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/RepositoryGroupConfiguration.java +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/RepositoryGroupConfiguration.java @@ -78,11 +78,37 @@ public class RepositoryGroupConfiguration */ private List<String> repositories; + /** + * The path for local data + */ + private String location; + //-----------/ //- Methods -/ //-----------/ + + /** + * Return the local path for group data. If the merged index property is set to a non absolute path, + * it is relative to this location. + * + * @return the path for group data storage + */ + public String getLocation( ) + { + return location; + } + + /** + * Set the local path for group data + * @param location + */ + public void setLocation( String location ) + { + this.location = location; + } + /** * Method addRepository. * diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java index f3f0f2ea7..27146f5e6 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java @@ -753,6 +753,8 @@ public class ConfigurationRegistryReader { value.setMergedIndexTtl(mergedIndexTtl); //String cronExpression = registry.getString( prefix + "cronExpression", value.getCronExpression() ); + value.setLocation( registry.getString( prefix + "location" ) ); + List<String> cronExpressionList = registry.getList(prefix + "cronExpression"); String cronExpression = value.getCronExpression(); if (cronExpressionList != null && !cronExpressionList.isEmpty()) { diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java index 2cbd387b6..83202f531 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java @@ -425,6 +425,9 @@ public class ConfigurationRegistryWriter { if (value.getType() != null) { registry.setString(prefix + "type", value.getType()); } + if (value.getLocation()!=null) { + registry.setString( prefix+"location", value.getType( ) ); + } if (value.getMergedIndexPath() != null && !value.getMergedIndexPath().equals(".indexer") ) { String mergedIndexPath = "mergedIndexPath"; diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/CheckedResult.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/CheckedResult.java new file mode 100644 index 000000000..41961a5e7 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/CheckedResult.java @@ -0,0 +1,31 @@ +package org.apache.archiva.repository; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +public interface CheckedResult<R extends Repository, D> +{ + R getRepository(); + + boolean isValid(); + + D getResult(); + +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryHandler.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryHandler.java new file mode 100644 index 000000000..49fc4de12 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryHandler.java @@ -0,0 +1,178 @@ +package org.apache.archiva.repository; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.configuration.Configuration; +import org.apache.archiva.repository.validation.RepositoryChecker; + +import java.util.Collection; +import java.util.Map; + +/** + * + * This is the generic interface that handles different repository flavours. + * + * @author Martin Stockhammer <martin_s@apache.org> + */ +public interface RepositoryHandler<R extends Repository, C> +{ + + /** + * Creates instances from the archiva configuration. The instances are not registered in the registry. + * + * @return A map of (repository id, Repository) pairs + */ + Map<String, R> newInstancesFromConfig(); + + /** + * Creates a new instance without registering and without updating the archiva configuration + * + * @param type the repository type + * @param id the repository identifier + * @return the repository instance + * @throws RepositoryException if the creation failed + */ + R newInstance(RepositoryType type, String id) throws RepositoryException; + + /** + * Creates a new instance and updates the given configuration object. + * + * @param repositoryConfiguration the configuration instance + * @return a newly created instance + * @throws RepositoryException if the creation failed + */ + R newInstance( C repositoryConfiguration ) throws RepositoryException; + + /** + * Adds the given repository to the registry or replaces a already existing repository in the registry. + * If an error occurred during the update, it will revert to the old repository status. + * + * @param repository the repository + * @return the created or updated repository instance + * @throws RepositoryException if the update or creation failed + */ + R put( R repository ) throws RepositoryException; + + /** + * Adds the repository to the registry, based on the given configuration. + * If there is a repository registered with the given id, it is updated. + * The archiva configuration is updated. The status is not defined, if an error occurs during update. The + * The repository instance is registered and initialized if no error occurs + * + * @param repositoryConfiguration the repository configuration + * @return the updated or created repository instance + * @throws RepositoryException if the update or creation failed + */ + R put( C repositoryConfiguration ) throws RepositoryException; + + /** + * Adds a repository from the given repository configuration. The changes are stored in + * the configuration object. The archiva registry is not updated. + * The returned repository instance is a clone of the registered repository instance. It is not registered + * and not initialized. References are not updated. + * + * @param repositoryConfiguration the repository configuration + * @param configuration the configuration instance + * @return the repository instance that was created or updated + * @throws RepositoryException if the update or creation failed + */ + R put( C repositoryConfiguration, Configuration configuration ) throws RepositoryException; + + /** + * Adds or updates a repository from the given configuration data. The resulting repository is + * checked by the repository checker and the result is returned. + * If the checker returns a valid result, the registry is updated and configuration is saved. + * + * @param repositoryConfiguration the repository configuration + * @param checker the checker that validates the repository data + * @return the repository and the check result + * @throws RepositoryException if the creation or update failed + */ + <D> CheckedResult<R, D> + putWithCheck( C repositoryConfiguration, RepositoryChecker<R, D> checker) throws RepositoryException; + + /** + * Removes the given repository from the registry and updates references and saves the new configuration. + * + * @param id The repository identifier + * @throws RepositoryException if the repository could not be removed + */ + void remove( final String id ) throws RepositoryException; + + /** + * Removes the given repository from the registry and updates only the given configuration instance. + * The archiva registry is not updated + * + * @param id the repository identifier + * @param configuration the configuration to update + * @throws RepositoryException if the repository could not be removed + */ + void remove( String id, Configuration configuration ) throws RepositoryException; + + /** + * Returns the repository with the given identifier or <code>null</code>, if it is not registered. + * + * @param id the repository id + * @return if the retrieval failed + */ + R get( String id ); + + /** + * Clones a given repository without registering. + * + * @param repo the repository that should be cloned + * @return a newly created instance with the same repository data + */ + R clone(R repo) throws RepositoryException; + + /** + * Updates the references and stores updates in the given <code>configuration</code> instance. + * The references that are updated depend on the concrete repository subclass <code>R</code>. + * This method may register/unregister repositories depending on the implementation. That means there is no simple + * way to roll back, if an error occurs. + * + * @param repo the repository for which references are updated + * @param repositoryConfiguration the repository configuration + */ + void updateReferences( R repo, C repositoryConfiguration ) throws RepositoryException; + + /** + * Returns all registered repositories. + * + * @return the list of repositories + */ + Collection<R> getAll(); + + /** + * Returns <code>true</code>, if the repository is registered with the given id, otherwise <code>false</code> + * @param id the repository identifier + * @return <code>true</code>, if it is registered, otherwise <code>false</code> + */ + boolean has(String id); + + /** + * Initializes + */ + void init(); + + /** + * Closes the handler + */ + void close(); + +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java index bb6bbc98b..2a5210666 100644 --- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java @@ -29,23 +29,31 @@ import org.apache.archiva.indexer.ArchivaIndexManager; import org.apache.archiva.indexer.IndexUpdateFailedException; import org.apache.archiva.repository.metadata.MetadataReader; import org.apache.archiva.repository.storage.StorageAsset; +import org.apache.archiva.repository.validation.ValidationError; +import org.apache.archiva.repository.validation.ValidationResponse; import java.util.Collection; +import java.util.List; +import java.util.Map; /** * Registry for repositories. This is the central entry point for repositories. It provides methods for * retrieving, adding and removing repositories. * <p> - * The modification methods addXX and removeXX persist the changes immediately to the configuration. If the + * The modification methods putXX and removeXX without configuration object persist the changes immediately to the archiva configuration. If the * configuration save fails the changes are rolled back. + * </p> * <p> + * The modification methods with configuration object do only update the given configuration. The configuration is not saved. + * </p> * @author Martin Stockhammer <martin_s@apache.org> */ +@SuppressWarnings( "UnusedReturnValue" ) public interface RepositoryRegistry extends EventSource { /** * Set the configuration for the registry - * @param archivaConfiguration + * @param archivaConfiguration the archiva configuration instance */ void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration ); @@ -57,7 +65,7 @@ public interface RepositoryRegistry extends EventSource ArchivaIndexManager getIndexManager( RepositoryType type ); /** - * Returns the metadatareader for the given repository type + * Returns the metadata reader for the given repository type * @param type the repository type * @return the metadata reader instance */ @@ -75,63 +83,305 @@ public interface RepositoryRegistry extends EventSource */ Collection<ManagedRepository> getManagedRepositories( ); + /** + * Returns a collection of all registered remote repositories + * @return the collection of remote repositories + */ Collection<RemoteRepository> getRemoteRepositories( ); + /** + * Returns a collection of all registered repository groups. + * + * @return the collection of repository groups + */ Collection<RepositoryGroup> getRepositoryGroups( ); + /** + * Returns the repository (managed, remote, group) with the given id + * @param repoId the id of the repository + * @return the repository or <code>null</code> if no repository with this ID is registered. + */ Repository getRepository( String repoId ); + /** + * Returns the managed repository with the given id + * @param repoId the id of the repository + * @return the managed repository instance or <code>null</code>, if no managed repository with this ID is registered. + */ ManagedRepository getManagedRepository( String repoId ); + /** + * Returns the remote repository with the given id + * @param repoId the id of the repository + * @return the remote repository instance or <code>null</code>, if no remote repository with this ID is registered. + */ RemoteRepository getRemoteRepository( String repoId ); + /** + * Returns the repository group with the given id + * @param groupId the id of the repository group + * @return the repository group instance or <code>null</code>, if no repository group with this ID is registered. + */ RepositoryGroup getRepositoryGroup( String groupId ); + /** + * Returns <code>true</code>, if a repository with the given ID is registered, otherwise <code>false</code> + * @param repoId the ID of the repository + * @return <code>true</code>, if a repository with the given ID is registered, otherwise <code>false</code> + */ + boolean hasRepository(String repoId); + + /** + * Returns <code>true</code>, if a managed repository with the given ID is registered, otherwise <code>false</code> + * @param repoId the id of the managed repository + * @return <code>true</code>, if a managed repository with the given ID is registered, otherwise <code>false</code> + */ + boolean hasManagedRepository(String repoId); + + /** + * Returns <code>true</code>, if a remote repository with the given ID is registered, otherwise <code>false</code> + * @param repoId the id of the remote repository + * @return <code>true</code>, if a remote repository with the given ID is registered, otherwise <code>false</code> + */ + boolean hasRemoteRepository(String repoId); + + /** + * Returns <code>true</code>, if a repository group with the given ID is registered, otherwise <code>false</code> + * @param groupId the id of the repository group + * @return <code>true</code>, if a repository group with the given ID is registered, otherwise <code>false</code> + */ + boolean hasRepositoryGroup( String groupId ); + + /** + * Adds or updates the given managed repository. If a managed repository with the given id exists already, it is updated + * from the data of the given instance. Otherwise a new repository is created and updated by the data of the given instance. + * + * The archiva configuration is updated and saved after updating the registered repository instance. + * + * @param managedRepository the managed repository + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ ManagedRepository putRepository( ManagedRepository managedRepository ) throws RepositoryException; + /** + * Adds or updates the given managed repository. If a managed repository with the given id exists already, it is updated + * from the data of the given configuration. Otherwise a new repository is created and updated by the data of the given configuration. + * + * The archiva configuration is updated and saved after updating the registered repository instance. + * + * @param managedRepositoryConfiguration the managed repository configuration + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ ManagedRepository putRepository( ManagedRepositoryConfiguration managedRepositoryConfiguration ) throws RepositoryException; + /** + * Adds or updates the given managed repository. If a managed repository with the given id exists already, it is updated + * from the data of the given configuration. Otherwise a new repository is created and updated by the data of the given configuration. + * + * This method can be used, if the archiva configuration should not be saved. It will only update the given configuration object. + * + * @param managedRepositoryConfiguration the managed repository configuration + * @param configuration the archiva configuration that is updated + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ ManagedRepository putRepository( ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration ) throws RepositoryException; + /** + * Adds or updates the given repository group. If a repository group with the given id exists already, it is updated + * from the data of the given instance. Otherwise a new repository is created and updated by the data of the given instance. + * + * The archiva configuration is updated and saved after updating the registered repository instance. + * + * @param repositoryGroup the repository group + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RepositoryGroup putRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException; + /** + * Adds or updates the given repository group. If a repository group with the given id exists already, it is updated + * from the data of the given configuration. Otherwise a new repository is created and updated by the data of the given configuration. + * + * The archiva configuration is updated and saved after updating the registered repository instance. + * + * @param repositoryGroupConfiguration the repository group configuration + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException; + + /** + * This method creates or updates a repository by the given configuration. It uses the <code>validator</code> to check the + * result. If the validation is not successful, the repository will not be saved. + * + * @param configuration the repository configuration + * @return the result + */ + CheckedResult<RepositoryGroup, Map<String, List<ValidationError>>> putRepositoryGroupAndValidate( RepositoryGroupConfiguration configuration) throws RepositoryException; + + /** + * Adds or updates the given repository group. If a repository group with the given id exists already, it is updated + * from the data of the given configuration. Otherwise a new repository is created and updated by the data of the given configuration. + * + * This method can be used, if the archiva configuration should not be saved. It will only update the given configuration object. + * + * @param repositoryGroupConfiguration the repository group configuration + * @param configuration the archiva configuration that is updated + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException; + /** + * Adds or updates the given remote repository. If a remote repository with the given id exists already, it is updated + * from the data of the given instance. Otherwise a new repository is created and updated by the data of the given instance. + * + * This method can be used, if the archiva configuration should not be saved. It will only update the given configuration object. + * + * @param remoteRepository the remote repository + * @param configuration the configuration that is updated + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RemoteRepository putRepository( RemoteRepository remoteRepository, Configuration configuration ) throws RepositoryException; + /** + * Adds or updates the given remote repository. If a remote repository with the given id exists already, it is updated + * from the data of the given instance. Otherwise a new repository is created and updated by the data of the given instance. + * + * The archiva configuration is updated and saved after updating the registered repository instance. + * + * @param remoteRepository the remote repository + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RemoteRepository putRepository( RemoteRepository remoteRepository ) throws RepositoryException; + /** + * Adds or updates the given remote repository. If a remote repository with the given id exists already, it is updated + * from the data of the given configuration. Otherwise a new repository is created and updated by the data of the given configuration. + * + * The archiva configuration is updated and saved after updating the registered repository instance. + * + * @param remoteRepositoryConfiguration the remote repository configuration + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RemoteRepository putRepository( RemoteRepositoryConfiguration remoteRepositoryConfiguration ) throws RepositoryException; + /** + * Adds or updates the given remote repository. If a remote repository with the given id exists already, it is updated + * from the data of the given configuration. Otherwise a new repository is created and updated by the data of the given configuration. + * + * This method can be used, if the archiva configuration should not be saved. It will only update the given configuration object. + * + * @param remoteRepositoryConfiguration the remote repository configuration + * @param configuration the archiva configuration where the updated data is stored into + * @return the repository instance, that was created or updated + * @throws RepositoryException if an error occurred while creating or updating the instance + */ RemoteRepository putRepository( RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration ) throws RepositoryException; + /** + * Removes the repository or repository group with the given id, if it exists. Otherwise, it will do nothing. + * + * The configuration is updated and saved, if the deletion was successful + * + * @param repoId the id of the repository or repository group to delete + * @throws RepositoryException if the repository deletion failed + */ void removeRepository( String repoId ) throws RepositoryException; + /** + * Removes the given repository. + * + * The configuration is updated and saved, if the deletion was successful + * + * @param repo the repository instance that should be deleted + * @throws RepositoryException if the repository deletion failed + */ void removeRepository( Repository repo ) throws RepositoryException; + /** + * Removes the given managed repository. + * + * The configuration is updated and saved, if the deletion was successful + * + * @param managedRepository the managed repository to remove + * @throws RepositoryException if the repository deletion failed + */ void removeRepository( ManagedRepository managedRepository ) throws RepositoryException; + /** + * Removes the given managed repository. The given configuration instance is updated, but the + * archiva configuration is not saved. + * + * @param managedRepository the managed repository to remove + * @param configuration the configuration instance to update + * @throws RepositoryException if the repository deletion failed + */ void removeRepository( ManagedRepository managedRepository, Configuration configuration ) throws RepositoryException; + /** + * Removes the given repository group. + * + * The configuration is updated and saved, if the deletion was successful + * + * @param repositoryGroup the repository group to remove + * @throws RepositoryException if the repository deletion failed + */ void removeRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException; + /** + * Removes the given repository group. The given configuration instance is updated, but the + * archiva configuration is not saved. + * + * @param repositoryGroup the repository group to remove + * @param configuration the configuration instance to update + * @throws RepositoryException if the repository deletion failed + */ void removeRepositoryGroup( RepositoryGroup repositoryGroup, Configuration configuration ) throws RepositoryException; + /** + * Removes the given remote repository. + * + * The configuration is updated and saved, if the deletion was successful + * + * @param remoteRepository the remote repository to remove + * @throws RepositoryException if the repository deletion failed + */ void removeRepository( RemoteRepository remoteRepository ) throws RepositoryException; + /** + * Removes the given remote repository. The given configuration instance is updated, but the + * archiva configuration is not saved. + * + * @param remoteRepository the remote repository to remove + * @param configuration the configuration instance to update + * @throws RepositoryException if the repository deletion failed + */ void removeRepository( RemoteRepository remoteRepository, Configuration configuration ) throws RepositoryException; + /** + * Reloads all repositories and groups from the configuration + */ void reload( ); void resetIndexingContext( Repository repository ) throws IndexUpdateFailedException; - ManagedRepository clone( ManagedRepository repo, String newId ) throws RepositoryException; - - <T extends Repository> Repository clone( T repo, String newId ) throws RepositoryException; - - RemoteRepository clone( RemoteRepository repo, String newId ) throws RepositoryException; + /** + * Creates a new repository based on the given repository and with the given new id. + * @param repo the repository to copy from + * @param newId the new repository id + * @param <T> the type of the repository (Manage, Remote or RepositoryGroup) + * @return the newly created repository + * @throws RepositoryException if the repository could not be created + */ + <T extends Repository> T clone( T repo, String newId ) throws RepositoryException; /** * Return the repository that stores the given asset. @@ -139,4 +389,25 @@ public interface RepositoryRegistry extends EventSource * @return the repository or <code>null</code> if no matching repository is found */ Repository getRepositoryOfAsset( StorageAsset asset ); + + /** + * Validates the set attributes of the given repository instance and returns the validation result. + * The repository registry uses all available validators and applies their validateRepository method to the given + * repository. Validation results will be merged per field. + * + * @param repository the repository to validate against + * @return the result of the validation. + */ + <R extends Repository> ValidationResponse<R> validateRepository( R repository); + + /** + * Validates the set attributes of the given repository instance for a repository update and returns the validation result. + * The repository registry uses all available validators and applies their validateRepositoryForUpdate method to the given + * repository. Validation results will be merged per field. + * + * @param repository the repository to validate against + * @return the result of the validation. + */ + <R extends Repository> ValidationResponse<R> validateRepositoryForUpdate( R repository); + } diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryType.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryType.java index f60c65787..b5e8385c1 100644 --- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryType.java +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryType.java @@ -25,5 +25,5 @@ package org.apache.archiva.repository; */ public enum RepositoryType { - MAVEN, NPM + ALL, MAVEN, NPM } diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/AbstractRepositoryValidator.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/AbstractRepositoryValidator.java new file mode 100644 index 000000000..319fc50db --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/AbstractRepositoryValidator.java @@ -0,0 +1,50 @@ +package org.apache.archiva.repository.validation; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.repository.CheckedResult; +import org.apache.archiva.repository.Repository; +import org.apache.archiva.repository.RepositoryRegistry; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +public abstract class AbstractRepositoryValidator<R extends Repository> implements RepositoryValidator<R> +{ + protected RepositoryRegistry repositoryRegistry; + + @Override + public void setRepositoryRegistry( RepositoryRegistry repositoryRegistry ) + { + this.repositoryRegistry = repositoryRegistry; + } + + protected abstract ValidationResponse<R> apply( R repo, boolean update ); + + @Override + public ValidationResponse<R> apply( R r ) + { + return apply( r, false ); + } + + @Override + public ValidationResponse<R> applyForUpdate( R repo ) + { + return apply( repo, true ); + } +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/RepositoryChecker.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/RepositoryChecker.java new file mode 100644 index 000000000..f208d10fe --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/RepositoryChecker.java @@ -0,0 +1,36 @@ +package org.apache.archiva.repository.validation; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.repository.CheckedResult; +import org.apache.archiva.repository.Repository; + +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +public interface RepositoryChecker<R extends Repository, D> extends Function<R, CheckedResult<R,D>> +{ + + @Override + CheckedResult<R,D> apply( R r ); + + CheckedResult<R,D> applyForUpdate( R repo ); +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/RepositoryValidator.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/RepositoryValidator.java new file mode 100644 index 000000000..041fe8a48 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/RepositoryValidator.java @@ -0,0 +1,85 @@ +package org.apache.archiva.repository.validation; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.repository.Repository; +import org.apache.archiva.repository.RepositoryRegistry; +import org.apache.archiva.repository.RepositoryType; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * A repository validator validates given repository data against certain rules. + * + * @author Martin Stockhammer <martin_s@apache.org> + */ +public interface RepositoryValidator<R extends Repository> extends RepositoryChecker<R, Map<String, List<ValidationError>>>, Comparable<RepositoryValidator<R>> +{ + + int DEFAULT_PRIORITY=1000; + + /** + * Returns the repository type for which this validator can be used. If the validator is applicable + * to all types, it should return {@link RepositoryType#ALL} + * + * @return the repository type for which this validator is applicable + */ + default RepositoryType getType() { + return RepositoryType.ALL; + } + + /** + * Returns the priority of this validator. Smaller values mean higher priority. + * All common validators have priority {@link #DEFAULT_PRIORITY} + * + * Validators are called in numerical order of their priority. + * + * @return + */ + default int getPriority() { + return DEFAULT_PRIORITY; + } + + + /** + * Orders by priority + * + * @see Comparable#compareTo(Object) + */ + @Override + default int compareTo( RepositoryValidator o ) { + if (o==null) { + return 1; + } else + { + return this.getPriority( ) - o.getPriority( ); + } + } + + /** + * Sets the repository registry to the given instance. + * @param repositoryRegistry the repository registry + */ + void setRepositoryRegistry( RepositoryRegistry repositoryRegistry ); + + Class<R> getFlavour(); + + boolean isFlavour(Class<?> clazz); +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/ValidationError.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/ValidationError.java new file mode 100644 index 000000000..22594fbfa --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/ValidationError.java @@ -0,0 +1,186 @@ +package org.apache.archiva.repository.validation; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents a single validation error. A error is defined by a global unique key and has a optional number + * of arguments. + * <p> + * The unique key should represent a category, the attribute and a generic type, separated by '.' + * E.g. repository_group.id.empty + * </p> + * <p> + * Categories normally separate errors for different domain types, like managed repository, repository group, maven repository. + * <p> + * Types define a certain type of error that can be handled similar independent of the attribute or category + * + * @author Martin Stockhammer <martin_s@apache.org> + */ +public class ValidationError +{ + public static final String UNSPECIFIED = "unspecified"; + + final String errorKey; + final String attribute; + final String category; + final String type; + final List<Object> arguments = new ArrayList<>(); + + + public static ValidationError ofKey( final String errorKey, Object... arguments ) + { + return new ValidationError( errorKey, getCategoryFromKey( errorKey ), getTypeFromKey( errorKey ), getAttributeFromKey( errorKey ), + Arrays.asList( arguments ) ); + } + + public static ValidationError ofKey( String errorKey, List<Object> arguments ) + { + return new ValidationError( errorKey, getCategoryFromKey( errorKey ), getTypeFromKey( errorKey ), getAttributeFromKey( errorKey ), + arguments ); + } + + public ValidationError( String errorKey, String category, String type, String attribute, List<Object> arguments ) + { + if ( StringUtils.isEmpty( errorKey ) ) + { + throw new IllegalArgumentException( "The key of a validation error cannot be empty" ); + } + this.errorKey = errorKey; + if ( arguments != null ) + { + this.arguments.addAll( arguments ); + } + this.type = type; + this.category = category; + this.attribute = attribute; + } + + private static String getTypeFromKey( final String errorKey ) + { + return errorKey.contains( "." ) ? + StringUtils.substringAfterLast( errorKey, "." ) : + UNSPECIFIED; + } + + private static String getCategoryFromKey( final String errorKey ) + { + return errorKey.contains( "." ) ? + StringUtils.substringBefore( errorKey, "." ) : + UNSPECIFIED; + } + + private static String getAttributeFromKey( final String errorKey ) + { + return StringUtils.countMatches( errorKey, "." ) >= 2 ? + StringUtils.substringBetween( errorKey, "." ) : UNSPECIFIED; + } + + /** + * Returns the unique key of this validation error. It is best practice for keys to contain the + * validation source, the attribute and a unique error definition. + * E.g. repository_group.id.empty + * + * @return + */ + public String getErrorKey( ) + { + return errorKey; + } + + /** + * Returns the list of arguments stored for this error + * @return the list of arguments + */ + public List<Object> getArguments( ) + { + return arguments; + } + + /** + * Adds the given argument to the list + * @param argument the argument to add + */ + public void addArgument( Object argument ) + { + this.arguments.add( argument ); + } + + /** + * Returns the generic error type, this error represents. + * + * @return the error type or {@link #UNSPECIFIED} if not explicitly set. + */ + public String getType( ) + { + return type; + } + + /** + * Returns the category of the error. + * + * @return the category or {@link #UNSPECIFIED} if not explicitly set + */ + public String getCategory( ) + { + return category; + } + + /** + * Returns the attribute name + * @return the attribute name or {@link #UNSPECIFIED} if not explicitly set + */ + public String getAttribute( ) + { + return attribute; + } + + @Override + public String toString( ) + { + final StringBuilder sb = new StringBuilder( "ValidationError{" ); + sb.append( "errorKey='" ).append( errorKey ).append( '\'' ); + sb.append( '}' ); + return sb.toString( ); + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) return true; + if ( o == null || getClass( ) != o.getClass( ) ) return false; + + ValidationError that = (ValidationError) o; + + if ( !errorKey.equals( that.errorKey ) ) return false; + return arguments.equals( that.arguments ); + } + + @Override + public int hashCode( ) + { + int result = errorKey.hashCode( ); + result = 31 * result + arguments.hashCode( ); + return result; + } +} diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/ValidationResponse.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/ValidationResponse.java new file mode 100644 index 000000000..377a3ccb2 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/validation/ValidationResponse.java @@ -0,0 +1,110 @@ +package org.apache.archiva.repository.validation; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.repository.CheckedResult; +import org.apache.archiva.repository.Repository; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * A validation response gives information about the validation status for certain attributes. + * + * + * @author Martin Stockhammer <martin_s@apache.org> + */ +public class ValidationResponse<R extends Repository> implements CheckedResult<R, Map<String, List<ValidationError>>> +{ + final boolean valid; + final R repository; + final Map<String, List<ValidationError>> validationErrors = new HashMap<>( ); + + + public ValidationResponse( R repo, Map<String, List<ValidationError>> errors) + { + if( errors==null || errors.size()==0 ) { + this.valid = true; + } else { + this.valid = false; + validationErrors.putAll( errors ); + } + this.repository = repo; + } + + public static <S extends Repository> ValidationResponse<S> getValid( S repository ) + { + return new ValidationResponse<>( repository, null ); + } + + @Override + public R getRepository( ) + { + return repository; + } + + /** + * Returns true, if the validation was successful and there are not validation errors. + * @return <code>true</code>, if the validation was successful, otherwise <code>false</code> + */ + @Override + public boolean isValid( ) + { + return valid; + } + + @Override + public Map<String, List<ValidationError>> getResult( ) + { + return validationErrors; + } + + + /** + * Add the given validation error to the list for the given attribute. + * + * @param attribute the name of the attribute + * @param error the error that is added to the list + */ + public void addValidationError(String attribute, ValidationError error) { + if (!validationErrors.containsKey( attribute )) { + validationErrors.put( attribute, new ArrayList<>( ) ); + } + validationErrors.get( attribute ).add( error ); + } + + /** + * Returns a list of validation errors that are stored for the given attribute. If there are no + * errors stored for this attribute, a empty list is returned. + * + * @param attribute the name of the attribute + * @return the list of validation errors + */ + public List<ValidationError> getValidationErrors(String attribute) { + if (validationErrors.containsKey( attribute )) { + return validationErrors.get( attribute ); + } else { + return Collections.emptyList( ); + } + } + +} diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistry.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistry.java index 249436092..db1d626a2 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistry.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistry.java @@ -38,6 +38,7 @@ import org.apache.archiva.indexer.ArchivaIndexingContext; import org.apache.archiva.indexer.IndexCreationFailedException; import org.apache.archiva.indexer.IndexManagerFactory; import org.apache.archiva.indexer.IndexUpdateFailedException; +import org.apache.archiva.repository.CheckedResult; import org.apache.archiva.repository.EditableManagedRepository; import org.apache.archiva.repository.EditableRemoteRepository; import org.apache.archiva.repository.EditableRepository; @@ -59,6 +60,11 @@ import org.apache.archiva.repository.features.IndexCreationFeature; import org.apache.archiva.repository.features.StagingRepositoryFeature; import org.apache.archiva.repository.metadata.MetadataReader; import org.apache.archiva.repository.storage.StorageAsset; +import org.apache.archiva.repository.validation.RepositoryChecker; +import org.apache.archiva.repository.validation.RepositoryValidator; +import org.apache.archiva.repository.validation.ValidationError; +import org.apache.archiva.repository.validation.ValidationResponse; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,6 +82,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @@ -98,7 +105,6 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa { private static final Logger log = LoggerFactory.getLogger( RepositoryRegistry.class ); - private final ConfigurationHandler configurationHandler; /** * We inject all repository providers @@ -129,16 +135,52 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock( ); private RepositoryGroupHandler groupHandler; + private final Set<RepositoryValidator<? extends Repository>> validators; + private final RepositoryChecker<RepositoryGroup, Map<String, List<ValidationError>>> groupChecker; + private final RepositoryChecker<ManagedRepository, Map<String, List<ValidationError>>> managedChecker; + private final RepositoryChecker<RemoteRepository, Map<String, List<ValidationError>>> remoteChecker; + private final ConfigurationHandler configurationHandler; + private AtomicBoolean groups_initalized = new AtomicBoolean( false ); private AtomicBoolean managed_initialized = new AtomicBoolean( false ); private AtomicBoolean remote_initialized = new AtomicBoolean( false ); - public ArchivaRepositoryRegistry( ConfigurationHandler configurationHandler ) + public ArchivaRepositoryRegistry( ConfigurationHandler configurationHandler, List<RepositoryValidator<? extends Repository>> validatorList ) { this.eventManager = new EventManager( this ); this.configurationHandler = configurationHandler; + this.validators = initValidatorList( validatorList ); + this.groupChecker = initChecker( RepositoryGroup.class ); + this.managedChecker = initChecker( ManagedRepository.class ); + this.remoteChecker = initChecker( RemoteRepository.class ); + } + + private <R extends Repository> RepositoryChecker<R, Map<String, List<ValidationError>>> initChecker(Class<R> clazz) { + return new RepositoryChecker<R, Map<String, List<ValidationError>>>( ) + { + @Override + public CheckedResult<R, Map<String, List<ValidationError>>> apply( R repositoryGroup ) + { + return this.apply( repositoryGroup ); + } + + @Override + public CheckedResult<R, Map<String, List<ValidationError>>> applyForUpdate( R repo ) + { + return this.applyForUpdate( repo ); + } + }; + } + + private Set<RepositoryValidator<? extends Repository>> initValidatorList( List<RepositoryValidator<? extends Repository>> validators ) + { + TreeSet<RepositoryValidator<? extends Repository>> val = new TreeSet<>( ); + for (RepositoryValidator<? extends Repository> validator : validators) { + val.add( validator ); + } + return val; } @Override @@ -513,7 +555,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa rwLock.readLock( ).lock( ); try { - return groupHandler.getRepositoryGroups( ); + return groupHandler.getAll( ); } finally { @@ -545,9 +587,9 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa log.debug( "Remote repo" ); return remoteRepositories.get( repoId ); } - else if ( groupHandler.hasRepositoryGroup( repoId ) ) + else if ( groupHandler.has( repoId ) ) { - return groupHandler.getRepositoryGroup( repoId ); + return groupHandler.get( repoId ); } else { @@ -608,7 +650,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa rwLock.readLock( ).lock( ); try { - return groupHandler.getRepositoryGroup( groupId ); + return groupHandler.get( groupId ); } finally { @@ -616,6 +658,30 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa } } + @Override + public boolean hasRepository( String repoId ) + { + return this.managedRepositories.containsKey( repoId ) || this.remoteRepositories.containsKey( repoId ) || groupHandler.has( repoId ); + } + + @Override + public boolean hasManagedRepository( String repoId ) + { + return this.managedRepositories.containsKey( repoId ); + } + + @Override + public boolean hasRemoteRepository( String repoId ) + { + return this.remoteRepositories.containsKey( repoId ); + } + + @Override + public boolean hasRepositoryGroup( String groupId ) + { + return groupHandler.has( groupId ); + } + protected void saveConfiguration( Configuration configuration ) throws IndeterminateConfigurationException, RegistryException { configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG ); @@ -805,7 +871,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa { throw new RepositoryException( "Fatal error. RepositoryGroupHandler not registered!" ); } - return this.groupHandler.putRepositoryGroup( repositoryGroup ); + return this.groupHandler.put( repositoryGroup ); } finally { @@ -827,7 +893,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa rwLock.writeLock( ).lock( ); try { - return groupHandler.putRepositoryGroup( repositoryGroupConfiguration ); + return groupHandler.put( repositoryGroupConfiguration ); } finally { @@ -836,6 +902,21 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa } + @Override + public CheckedResult<RepositoryGroup, Map<String, List<ValidationError>>> putRepositoryGroupAndValidate( RepositoryGroupConfiguration repositoryGroupConfiguration ) + throws RepositoryException + { + rwLock.writeLock( ).lock( ); + try + { + return groupHandler.putWithCheck( repositoryGroupConfiguration, this.groupChecker ); + } + finally + { + rwLock.writeLock( ).unlock( ); + } + } + /** * Adds a new repository group or updates the repository group with the same id. The given configuration object is updated, but * the configuration is not saved. @@ -851,7 +932,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa rwLock.writeLock( ).lock( ); try { - return groupHandler.putRepositoryGroup( repositoryGroupConfiguration, configuration ); + return groupHandler.put( repositoryGroupConfiguration, configuration ); } finally { @@ -1234,12 +1315,12 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa return; } final String id = repositoryGroup.getId( ); - if ( groupHandler.hasRepositoryGroup( id ) ) + if ( groupHandler.has( id ) ) { rwLock.writeLock( ).lock( ); try { - groupHandler.removeRepositoryGroup( id ); + groupHandler.remove( id ); } finally { @@ -1256,12 +1337,12 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa return; } final String id = repositoryGroup.getId( ); - if ( groupHandler.hasRepositoryGroup( id ) ) + if ( groupHandler.has( id ) ) { rwLock.writeLock( ).lock( ); try { - groupHandler.removeRepositoryGroup( id, configuration ); + groupHandler.remove( id, configuration ); } finally { @@ -1395,7 +1476,6 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa * @param repo The origin repository * @return The cloned repository. */ - @Override public ManagedRepository clone( ManagedRepository repo, String newId ) throws RepositoryException { if ( managedRepositories.containsKey( newId ) || remoteRepositories.containsKey( newId ) ) @@ -1411,15 +1491,15 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa } @Override - public <T extends Repository> Repository clone( T repo, String newId ) throws RepositoryException + public <T extends Repository> T clone( T repo, String newId ) throws RepositoryException { if ( repo instanceof RemoteRepository ) { - return this.clone( (RemoteRepository) repo, newId ); + return (T) this.clone( (RemoteRepository) repo, newId ); } else if ( repo instanceof ManagedRepository ) { - return this.clone( (ManagedRepository) repo, newId ); + return (T) this.clone( (ManagedRepository) repo, newId ); } else { @@ -1434,7 +1514,6 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa * @param repo The origin repository * @return The cloned repository. */ - @Override public RemoteRepository clone( RemoteRepository repo, String newId ) throws RepositoryException { if ( managedRepositories.containsKey( newId ) || remoteRepositories.containsKey( newId ) ) @@ -1463,6 +1542,35 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa } } + @Override + public <R extends Repository> ValidationResponse<R> validateRepository( R repository ) + { + Map<String, List<ValidationError>> errorMap = this.validators.stream( ) + .filter( ( validator ) -> validator.getType( ).equals( RepositoryType.ALL ) || repository.getType( ).equals( validator.getType( ) ) ) + .filter( val -> val.isFlavour( repository.getClass() )) + .flatMap( validator -> ((RepositoryValidator<R>)validator).apply( repository ).getResult().entrySet( ).stream( ) ) + .collect( Collectors.toMap( + entry -> entry.getKey( ), + entry -> entry.getValue( ), + ( list1, list2 ) -> ListUtils.union( list1, list2 ) + ) ); + return new ValidationResponse( repository, errorMap ); + } + + @Override + public <R extends Repository> ValidationResponse<R> validateRepositoryForUpdate( R repository ) + { + Map<String, List<ValidationError>> errorMap = this.validators.stream( ) + .filter( ( validator ) -> validator.getType( ).equals( RepositoryType.ALL ) || repository.getType( ).equals( validator.getType( ) ) ) + .filter( val -> val.isFlavour( repository.getClass() )) + .flatMap( validator -> ((RepositoryValidator<R>)validator).applyForUpdate( repository ).getResult().entrySet( ).stream( ) ) + .collect( Collectors.toMap( + entry -> entry.getKey( ), + entry -> entry.getValue( ), + ( list1, list2 ) -> ListUtils.union( list1, list2 ) + ) ); + return new ValidationResponse( repository, errorMap ); + } @Override public void configurationEvent( ConfigurationEvent event ) @@ -1509,22 +1617,25 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa { RepositoryIndexEvent idxEvent = event; EditableRepository repo = (EditableRepository) idxEvent.getRepository( ); - if ( repo != null ) { + if ( repo != null ) + { ArchivaIndexManager idxmgr = getIndexManager( repo.getType( ) ); if ( repo.getIndexingContext( ) != null ) { try { - ArchivaIndexingContext newCtx = idxmgr.move( repo.getIndexingContext( ), repo ); - repo.setIndexingContext( newCtx ); - idxmgr.updateLocalIndexPath( repo ); + ArchivaIndexingContext newCtx = idxmgr.move( repo.getIndexingContext( ), repo ); + repo.setIndexingContext( newCtx ); + idxmgr.updateLocalIndexPath( repo ); } catch ( IndexCreationFailedException e ) { log.error( "Could not move index to new directory: '{}'", e.getMessage( ), e ); } - } else { + } + else + { try { ArchivaIndexingContext context = idxmgr.createContext( repo ); diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ConfigurationHandler.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ConfigurationHandler.java index c4ee51370..4c21083e9 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ConfigurationHandler.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ConfigurationHandler.java @@ -24,6 +24,8 @@ import org.apache.archiva.configuration.ConfigurationListener; import org.apache.archiva.configuration.IndeterminateConfigurationException; import org.springframework.stereotype.Service; +import java.util.concurrent.locks.ReentrantReadWriteLock; + /** * This is just a simple wrapper to access the archiva configuration used by the registry and associated classes * @@ -36,6 +38,8 @@ public class ConfigurationHandler private ArchivaConfiguration archivaConfiguration; + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock( ); + public ConfigurationHandler( ArchivaConfiguration archivaConfiguration ) { this.archivaConfiguration = archivaConfiguration; } @@ -68,4 +72,7 @@ public class ConfigurationHandler archivaConfiguration.save( configuration, "" ); } + ReentrantReadWriteLock getLock() { + return lock; + } } diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/RepositoryGroupHandler.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/RepositoryGroupHandler.java index 9a08e76ae..376be6665 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/RepositoryGroupHandler.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/RepositoryGroupHandler.java @@ -20,18 +20,22 @@ package org.apache.archiva.repository.base; import org.apache.archiva.components.registry.RegistryException; import org.apache.archiva.configuration.Configuration; import org.apache.archiva.configuration.IndeterminateConfigurationException; +import org.apache.archiva.configuration.ManagedRepositoryConfiguration; import org.apache.archiva.configuration.RepositoryGroupConfiguration; import org.apache.archiva.indexer.merger.MergedRemoteIndexesScheduler; +import org.apache.archiva.repository.CheckedResult; import org.apache.archiva.repository.EditableRepository; import org.apache.archiva.repository.EditableRepositoryGroup; import org.apache.archiva.repository.ManagedRepository; import org.apache.archiva.repository.RepositoryException; import org.apache.archiva.repository.RepositoryGroup; +import org.apache.archiva.repository.RepositoryHandler; import org.apache.archiva.repository.RepositoryProvider; import org.apache.archiva.repository.RepositoryType; import org.apache.archiva.repository.event.RepositoryEvent; import org.apache.archiva.repository.features.IndexCreationFeature; import org.apache.archiva.repository.storage.StorageAsset; +import org.apache.archiva.repository.validation.RepositoryChecker; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +53,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH; @@ -59,52 +64,60 @@ import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH; * * @author Martin Stockhammer <martin_s@apache.org> */ -@Service("repositoryGroupHandler#default") -public class RepositoryGroupHandler +@Service( "repositoryGroupHandler#default" ) +public class RepositoryGroupHandler implements RepositoryHandler<RepositoryGroup, RepositoryGroupConfiguration> { - private static final Logger log = LoggerFactory.getLogger(RepositoryGroupHandler.class); + private static final Logger log = LoggerFactory.getLogger( RepositoryGroupHandler.class ); private final ArchivaRepositoryRegistry repositoryRegistry; private final ConfigurationHandler configurationHandler; private final MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler; - private Map<String, RepositoryGroup> repositoryGroups = new HashMap<>(); + private final Map<String, RepositoryGroup> repositoryGroups = new HashMap<>( ); private Path groupsDirectory; /** * Creates a new instance. All dependencies are injected on the constructor. - * @param repositoryRegistry the registry. To avoid circular dependencies via DI, this class registers itself on the registry. - * @param configurationHandler the configuration handler is used to retrieve and save configuration. + * + * @param repositoryRegistry the registry. To avoid circular dependencies via DI, this class registers itself on the registry. + * @param configurationHandler the configuration handler is used to retrieve and save configuration. * @param mergedRemoteIndexesScheduler the index scheduler is used for merging the indexes from all group members */ public RepositoryGroupHandler( ArchivaRepositoryRegistry repositoryRegistry, ConfigurationHandler configurationHandler, - @Named("mergedRemoteIndexesScheduler#default") MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler) { + @Named( "mergedRemoteIndexesScheduler#default" ) MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler ) + { this.configurationHandler = configurationHandler; this.mergedRemoteIndexesScheduler = mergedRemoteIndexesScheduler; this.repositoryRegistry = repositoryRegistry; } + @Override @PostConstruct - private void init() { + public void init( ) + { log.debug( "Initializing repository group handler " + repositoryRegistry.toString( ) ); + initializeStorage( ); // We are registering this class on the registry. This is necessary to avoid circular dependencies via injection. this.repositoryRegistry.registerGroupHandler( this ); - initializeStorage(); } - public void initializeFromConfig() { - this.repositoryGroups.clear(); - this.repositoryGroups.putAll( getRepositoryGroupsFromConfig( ) ); - for (RepositoryGroup group : this.repositoryGroups.values()) { + public void initializeFromConfig( ) + { + this.repositoryGroups.clear( ); + this.repositoryGroups.putAll( newInstancesFromConfig( ) ); + for ( RepositoryGroup group : this.repositoryGroups.values( ) ) + { initializeGroup( group ); } } - private void initializeStorage() { + private void initializeStorage( ) + { Path baseDir = this.configurationHandler.getArchivaConfiguration( ).getRepositoryGroupBaseDir( ); - if (!Files.exists( baseDir) ) { + if ( !Files.exists( baseDir ) ) + { try { Files.createDirectories( baseDir ); @@ -117,86 +130,115 @@ public class RepositoryGroupHandler this.groupsDirectory = baseDir; } - private void initializeGroup(RepositoryGroup repositoryGroup) { - StorageAsset indexDirectoy = getMergedIndexDirectory( repositoryGroup ); - if (!indexDirectoy.exists()) { + private void initializeGroup( RepositoryGroup repositoryGroup ) + { + StorageAsset indexDirectory = getMergedIndexDirectory( repositoryGroup ); + if ( !indexDirectory.exists( ) ) + { try { - indexDirectoy.create( ); + indexDirectory.create( ); } catch ( IOException e ) { - log.error( "Could not create index directory {} for group {}: {}", indexDirectoy, repositoryGroup.getId( ), e.getMessage( ) ); + log.error( "Could not create index directory {} for group {}: {}", indexDirectory, repositoryGroup.getId( ), e.getMessage( ) ); } } - Path groupPath = groupsDirectory.resolve(repositoryGroup.getId() ); - if ( !Files.exists(groupPath) ) + Path groupPath = groupsDirectory.resolve( repositoryGroup.getId( ) ); + if ( !Files.exists( groupPath ) ) { - try { - Files.createDirectories(groupPath); - } catch (IOException e) { - log.error("Could not create repository group directory {}", groupPath); + try + { + Files.createDirectories( groupPath ); + } + catch ( IOException e ) + { + log.error( "Could not create repository group directory {}", groupPath ); } } mergedRemoteIndexesScheduler.schedule( repositoryGroup, - indexDirectoy); + indexDirectory ); } public StorageAsset getMergedIndexDirectory( RepositoryGroup group ) { - if (group!=null) { - return group.getFeature( IndexCreationFeature.class).get().getLocalIndexPath(); - } else { + if ( group != null ) + { + return group.getFeature( IndexCreationFeature.class ).get( ).getLocalIndexPath( ); + } + else + { return null; } } - public Map<String, RepositoryGroup> getRepositoryGroupsFromConfig() { - try { + + @Override + public Map<String, RepositoryGroup> newInstancesFromConfig( ) + { + try + { List<RepositoryGroupConfiguration> repositoryGroupConfigurations = - this.configurationHandler.getBaseConfiguration().getRepositoryGroups(); + this.configurationHandler.getBaseConfiguration( ).getRepositoryGroups( ); - if (repositoryGroupConfigurations == null) { - return Collections.emptyMap(); + if ( repositoryGroupConfigurations == null ) + { + return Collections.emptyMap( ); } - Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>(repositoryGroupConfigurations.size()); - - Map<RepositoryType, RepositoryProvider> providerMap = repositoryRegistry.getRepositoryProviderMap(); - for (RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations) { - RepositoryType repositoryType = RepositoryType.valueOf(repoConfig.getType()); - if (providerMap.containsKey(repositoryType)) { - try { - RepositoryGroup repo = createNewRepositoryGroup(providerMap.get(repositoryType), repoConfig); - repositoryGroupMap.put(repo.getId(), repo); - } catch (Exception e) { - log.error("Could not create repository group {}: {}", repoConfig.getId(), e.getMessage(), e); + Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>( repositoryGroupConfigurations.size( ) ); + + Map<RepositoryType, RepositoryProvider> providerMap = repositoryRegistry.getRepositoryProviderMap( ); + for ( RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations ) + { + RepositoryType repositoryType = RepositoryType.valueOf( repoConfig.getType( ) ); + if ( providerMap.containsKey( repositoryType ) ) + { + try + { + RepositoryGroup repo = createNewRepositoryGroup( providerMap.get( repositoryType ), repoConfig ); + repositoryGroupMap.put( repo.getId( ), repo ); + } + catch ( Exception e ) + { + log.error( "Could not create repository group {}: {}", repoConfig.getId( ), e.getMessage( ), e ); } } } return repositoryGroupMap; - } catch (Throwable e) { - log.error("Could not initialize repositories from config: {}", e.getMessage(), e); - return Collections.emptyMap(); + } + catch ( Throwable e ) + { + log.error( "Could not initialize repositories from config: {}", e.getMessage( ), e ); + return Collections.emptyMap( ); } } - public RepositoryGroup createNewRepositoryGroup(RepositoryProvider provider, RepositoryGroupConfiguration config) throws RepositoryException + @Override + public RepositoryGroup newInstance( final RepositoryType type, String id ) throws RepositoryException { - RepositoryGroup repositoryGroup = provider.createRepositoryGroup(config); - repositoryGroup.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry); - updateRepositoryReferences(provider, repositoryGroup, config); - return repositoryGroup; + RepositoryProvider provider = repositoryRegistry.getProvider( type ); + RepositoryGroupConfiguration config = new RepositoryGroupConfiguration( ); + config.setId( id ); + return createNewRepositoryGroup( provider, config ); } - public void updateRepositoryReferences( RepositoryProvider provider, RepositoryGroup group, RepositoryGroupConfiguration configuration) { - if (group instanceof EditableRepositoryGroup ) { - EditableRepositoryGroup eGroup = (EditableRepositoryGroup) group; - eGroup.setRepositories(configuration.getRepositories().stream() - .map(r -> repositoryRegistry.getManagedRepository(r)).collect( Collectors.toList())); - } + @Override + public RepositoryGroup newInstance( final RepositoryGroupConfiguration repositoryConfiguration ) throws RepositoryException + { + RepositoryType type = RepositoryType.valueOf( repositoryConfiguration.getType( ) ); + RepositoryProvider provider = repositoryRegistry.getProvider( type ); + return createNewRepositoryGroup( provider, repositoryConfiguration ); } + private RepositoryGroup createNewRepositoryGroup( RepositoryProvider provider, RepositoryGroupConfiguration config ) throws RepositoryException + { + RepositoryGroup repositoryGroup = provider.createRepositoryGroup( config ); + updateReferences( repositoryGroup, config ); + return repositoryGroup; + } + + /** * Adds a new repository group to the current list, or replaces the repository group definition with * the same id, if it exists already. @@ -205,36 +247,56 @@ public class RepositoryGroupHandler * @param repositoryGroup the new repository group. * @throws RepositoryException if the new repository group could not be saved to the configuration. */ - public RepositoryGroup putRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException { - final String id = repositoryGroup.getId(); - RepositoryGroup originRepoGroup = repositoryGroups.put(id, repositoryGroup); - try { - if (originRepoGroup != null && originRepoGroup != repositoryGroup) { - this.mergedRemoteIndexesScheduler.unschedule( originRepoGroup ); - originRepoGroup.close(); - } - RepositoryProvider provider = repositoryRegistry.getProvider( repositoryGroup.getType()); - RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration(repositoryGroup); - Configuration configuration = this.configurationHandler.getBaseConfiguration(); - updateRepositoryReferences(provider, repositoryGroup, newCfg); - RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(id); - if (oldCfg != null) { - configuration.removeRepositoryGroup(oldCfg); + @Override + public RepositoryGroup put( final RepositoryGroup repositoryGroup ) throws RepositoryException + { + final String id = repositoryGroup.getId( ); + RepositoryGroup originRepoGroup = repositoryGroups.remove( id ); + try + { + if ( originRepoGroup != null && originRepoGroup != repositoryGroup ) + { + this.mergedRemoteIndexesScheduler.unschedule( originRepoGroup ); + originRepoGroup.close( ); + } + RepositoryProvider provider = repositoryRegistry.getProvider( repositoryGroup.getType( ) ); + RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration( repositoryGroup ); + ReentrantReadWriteLock.WriteLock configLock = this.configurationHandler.getLock( ).writeLock( ); + configLock.lock( ); + try + { + Configuration configuration = this.configurationHandler.getBaseConfiguration( ); + updateReferences( repositoryGroup, newCfg ); + RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( id ); + if ( oldCfg != null ) + { + configuration.removeRepositoryGroup( oldCfg ); } - configuration.addRepositoryGroup(newCfg); - repositoryRegistry.saveConfiguration(configuration); + configuration.addRepositoryGroup( newCfg ); + configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG ); initializeGroup( repositoryGroup ); - return repositoryGroup; - } catch (Exception e) { - // Rollback - if (originRepoGroup != null) { - repositoryGroups.put(id, originRepoGroup); - } else { - repositoryGroups.remove(id); - } - log.error("Exception during configuration update {}", e.getMessage(), e); - throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage())); } + finally + { + configLock.unlock( ); + } + repositoryGroups.put( id, repositoryGroup ); + return repositoryGroup; + } + catch ( Exception e ) + { + // Rollback + if ( originRepoGroup != null ) + { + repositoryGroups.put( id, originRepoGroup ); + } + else + { + repositoryGroups.remove( id ); + } + log.error( "Exception during configuration update {}", e.getMessage( ), e ); + throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ), e); + } } /** @@ -245,72 +307,153 @@ public class RepositoryGroupHandler * @return the updated or created repository * @throws RepositoryException if an error occurs, or the configuration is not valid. */ - public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException { - final String id = repositoryGroupConfiguration.getId(); - final RepositoryType repositoryType = RepositoryType.valueOf(repositoryGroupConfiguration.getType()); - Configuration configuration = this.configurationHandler.getBaseConfiguration(); - RepositoryGroup repositoryGroup = repositoryGroups.get(id); - RepositoryGroupConfiguration oldCfg = repositoryGroup != null ? repositoryRegistry.getProvider(repositoryType).getRepositoryGroupConfiguration(repositoryGroup) : null; - repositoryGroup = putRepositoryGroup(repositoryGroupConfiguration, configuration); - try { - repositoryRegistry.saveConfiguration(configuration); - } catch ( IndeterminateConfigurationException | RegistryException e) { - if (oldCfg != null) { - repositoryRegistry.getProvider(repositoryType).updateRepositoryGroupInstance((EditableRepositoryGroup) repositoryGroup, oldCfg); + @Override + public RepositoryGroup put( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException + { + final String id = repositoryGroupConfiguration.getId( ); + final RepositoryType repositoryType = RepositoryType.valueOf( repositoryGroupConfiguration.getType( ) ); + final RepositoryProvider provider = repositoryRegistry.getProvider( repositoryType ); + RepositoryGroup currentRepository; + ReentrantReadWriteLock.WriteLock configLock = this.configurationHandler.getLock( ).writeLock( ); + configLock.lock( ); + try + { + Configuration configuration = this.configurationHandler.getBaseConfiguration( ); + currentRepository = repositoryRegistry.getRepositoryGroup( id ); + RepositoryGroup oldRepository = currentRepository == null ? null : clone( currentRepository ); + try + { + + if (currentRepository==null) { + currentRepository = put( repositoryGroupConfiguration, configuration ); + } else + { + setRepositoryGroupDefaults( repositoryGroupConfiguration ); + provider.updateRepositoryGroupInstance( (EditableRepositoryGroup) currentRepository, repositoryGroupConfiguration ); } - log.error("Could not save the configuration for repository group {}: {}", id, e.getMessage(), e); - throw new RepositoryException("Could not save the configuration for repository group " + id + ": " + e.getMessage()); + configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG ); + updateReferences( currentRepository, repositoryGroupConfiguration ); + initializeGroup( currentRepository ); + this.repositoryGroups.put( id, currentRepository ); } - return repositoryGroup; + catch ( IndeterminateConfigurationException | RegistryException | RepositoryException e ) + { + // Trying a rollback + if ( oldRepository != null ) + { + RepositoryGroupConfiguration oldCfg = provider.getRepositoryGroupConfiguration( oldRepository ); + provider.updateRepositoryGroupInstance( (EditableRepositoryGroup) currentRepository, oldCfg); + replaceOrAddRepositoryConfig( oldCfg, configuration ); + try + { + configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG ); + } + catch ( IndeterminateConfigurationException | RegistryException indeterminateConfigurationException ) + { + log.error( "Fatal error, config save during rollback failed: {}", e.getMessage( ), e ); + } + updateReferences( oldRepository, oldCfg ); + initializeGroup( oldRepository ); + } + log.error( "Could not save the configuration for repository group {}: {}", id, e.getMessage( ), e ); + if (e instanceof RepositoryException) { + throw (RepositoryException) e; + } else + { + throw new RepositoryException( "Could not save the configuration for repository group " + id + ": " + e.getMessage( ) ); + } + } + } + finally + { + configLock.unlock( ); + } + return currentRepository; } - public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException { - final String id = repositoryGroupConfiguration.getId(); - final RepositoryType repoType = RepositoryType.valueOf(repositoryGroupConfiguration.getType()); - RepositoryGroup repo; - setRepositoryGroupDefaults(repositoryGroupConfiguration); - if (repositoryGroups.containsKey(id)) { - repo = repositoryGroups.get(id); - this.mergedRemoteIndexesScheduler.unschedule( repo ); - if (repo instanceof EditableRepositoryGroup) { - repositoryRegistry.getProvider(repoType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, repositoryGroupConfiguration); - } else { - throw new RepositoryException("The repository is not editable " + id); - } - } else { - repo = repositoryRegistry.getProvider(repoType).createRepositoryGroup(repositoryGroupConfiguration); - repositoryGroups.put(id, repo); + @Override + public RepositoryGroup put( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException + { + final String id = repositoryGroupConfiguration.getId( ); + final RepositoryType repoType = RepositoryType.valueOf( repositoryGroupConfiguration.getType( ) ); + RepositoryGroup repo; + setRepositoryGroupDefaults( repositoryGroupConfiguration ); + if ( repositoryGroups.containsKey( id ) ) + { + repo = clone( repositoryGroups.get( id ) ); + if ( repo instanceof EditableRepositoryGroup ) + { + repositoryRegistry.getProvider( repoType ).updateRepositoryGroupInstance( (EditableRepositoryGroup) repo, repositoryGroupConfiguration ); + } + else + { + throw new RepositoryException( "The repository is not editable " + id ); } - updateRepositoryReferences(repositoryRegistry.getProvider(repoType), repo, repositoryGroupConfiguration); - replaceOrAddRepositoryConfig(repositoryGroupConfiguration, configuration); - initializeGroup( repo ); - return repo; + } + else + { + repo = repositoryRegistry.getProvider( repoType ).createRepositoryGroup( repositoryGroupConfiguration ); + } + replaceOrAddRepositoryConfig( repositoryGroupConfiguration, configuration ); + return repo; } - private void setRepositoryGroupDefaults(RepositoryGroupConfiguration repositoryGroupConfiguration) { - if ( StringUtils.isEmpty(repositoryGroupConfiguration.getMergedIndexPath())) { - repositoryGroupConfiguration.setMergedIndexPath(DEFAULT_INDEX_PATH); + @Override + public <D> CheckedResult<RepositoryGroup, D> putWithCheck( RepositoryGroupConfiguration repositoryConfiguration, RepositoryChecker<RepositoryGroup, D> checker ) throws RepositoryException + { + final String id = repositoryConfiguration.getId( ); + RepositoryGroup currentGroup = repositoryGroups.get( id ); + Configuration configuration = configurationHandler.getBaseConfiguration( ); + RepositoryGroup repositoryGroup = put( repositoryConfiguration, configuration ); + CheckedResult<RepositoryGroup, D> result; + if ( currentGroup == null ) + { + result = checker.apply( repositoryGroup ); } - if (repositoryGroupConfiguration.getMergedIndexTtl() <= 0) { - repositoryGroupConfiguration.setMergedIndexTtl(300); + else + { + result = checker.applyForUpdate( repositoryGroup ); } - if (StringUtils.isEmpty(repositoryGroupConfiguration.getCronExpression())) { - repositoryGroupConfiguration.setCronExpression("0 0 03 ? * MON"); + if ( result.isValid( ) ) + { + put( repositoryConfiguration ); } + return result; } - private void replaceOrAddRepositoryConfig(RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) { - RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(repositoryGroupConfiguration.getId()); - if (oldCfg != null) { - configuration.removeRepositoryGroup(oldCfg); + + private void setRepositoryGroupDefaults( RepositoryGroupConfiguration repositoryGroupConfiguration ) + { + if ( StringUtils.isEmpty( repositoryGroupConfiguration.getMergedIndexPath( ) ) ) + { + repositoryGroupConfiguration.setMergedIndexPath( DEFAULT_INDEX_PATH ); + } + if ( repositoryGroupConfiguration.getMergedIndexTtl( ) <= 0 ) + { + repositoryGroupConfiguration.setMergedIndexTtl( 300 ); + } + if ( StringUtils.isEmpty( repositoryGroupConfiguration.getCronExpression( ) ) ) + { + repositoryGroupConfiguration.setCronExpression( "0 0 03 ? * MON" ); } - configuration.addRepositoryGroup(repositoryGroupConfiguration); } - public void removeRepositoryFromGroups( ManagedRepository repo) { - if (repo != null) { - repositoryGroups.values().stream().filter(repoGroup -> repoGroup instanceof EditableRepository ). - map(repoGroup -> (EditableRepositoryGroup) repoGroup).forEach(repoGroup -> repoGroup.removeRepository(repo)); + private void replaceOrAddRepositoryConfig( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) + { + RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( repositoryGroupConfiguration.getId( ) ); + if ( oldCfg != null ) + { + configuration.removeRepositoryGroup( oldCfg ); + } + configuration.addRepositoryGroup( repositoryGroupConfiguration ); + } + + public void removeRepositoryFromGroups( ManagedRepository repo ) + { + if ( repo != null ) + { + repositoryGroups.values( ).stream( ).filter( repoGroup -> repoGroup instanceof EditableRepository ). + map( repoGroup -> (EditableRepositoryGroup) repoGroup ).forEach( repoGroup -> repoGroup.removeRepository( repo ) ); } } @@ -321,75 +464,122 @@ public class RepositoryGroupHandler * @param id the id of the repository group to remove * @throws RepositoryException if a error occurs during configuration save */ - public void removeRepositoryGroup( final String id ) throws RepositoryException { - RepositoryGroup repo = getRepositoryGroup(id); - if (repo != null) { - try { - repo = repositoryGroups.remove(id); - if (repo != null) { + @Override + public void remove( final String id ) throws RepositoryException + { + RepositoryGroup repo = get( id ); + if ( repo != null ) + { + try + { + repo = repositoryGroups.remove( id ); + if ( repo != null ) + { this.mergedRemoteIndexesScheduler.unschedule( repo ); - repo.close(); - Configuration configuration = this.configurationHandler.getBaseConfiguration(); - RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id); - if (cfg != null) { - configuration.removeRepositoryGroup(cfg); + repo.close( ); + Configuration configuration = this.configurationHandler.getBaseConfiguration( ); + RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById( id ); + if ( cfg != null ) + { + configuration.removeRepositoryGroup( cfg ); } - this.configurationHandler.save(configuration, ConfigurationHandler.REGISTRY_EVENT_TAG ); + this.configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG ); } - } catch (RegistryException | IndeterminateConfigurationException e) { + } + catch ( RegistryException | IndeterminateConfigurationException e ) + { // Rollback - log.error("Could not save config after repository removal: {}", e.getMessage(), e); - repositoryGroups.put(repo.getId(), repo); - throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage()); + log.error( "Could not save config after repository removal: {}", e.getMessage( ), e ); + repositoryGroups.put( repo.getId( ), repo ); + throw new RepositoryException( "Could not save configuration after repository removal: " + e.getMessage( ) ); } } } - public void removeRepositoryGroup( String id, Configuration configuration ) throws RepositoryException { - RepositoryGroup repo = repositoryGroups.get(id); - if (repo != null) { - repo = repositoryGroups.remove(id); - if (repo != null) { - this.mergedRemoteIndexesScheduler.unschedule( repo ); - repo.close(); - RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id); - if (cfg != null) { - configuration.removeRepositoryGroup(cfg); - } + @Override + public void remove( String id, Configuration configuration ) throws RepositoryException + { + RepositoryGroup repo = repositoryGroups.get( id ); + if ( repo != null ) + { + repo = repositoryGroups.remove( id ); + if ( repo != null ) + { + this.mergedRemoteIndexesScheduler.unschedule( repo ); + repo.close( ); + RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById( id ); + if ( cfg != null ) + { + configuration.removeRepositoryGroup( cfg ); } + } } } - public RepositoryGroup getRepositoryGroup( String groupId ) { - return repositoryGroups.get(groupId); + @Override + public RepositoryGroup get( String groupId ) + { + return repositoryGroups.get( groupId ); } - public Collection<RepositoryGroup> getRepositoryGroups() { + @Override + public RepositoryGroup clone( RepositoryGroup repo ) throws RepositoryException + { + RepositoryProvider provider = repositoryRegistry.getProvider( repo.getType( ) ); + RepositoryGroupConfiguration cfg = provider.getRepositoryGroupConfiguration( repo ); + RepositoryGroup cloned = provider.createRepositoryGroup( cfg ); + cloned.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry ); + return cloned; + } + + @Override + public void updateReferences( RepositoryGroup repo, RepositoryGroupConfiguration repositoryConfiguration ) throws RepositoryException + { + if ( repo instanceof EditableRepositoryGroup ) + { + EditableRepositoryGroup eGroup = (EditableRepositoryGroup) repo; + eGroup.setRepositories( repositoryConfiguration.getRepositories( ).stream( ) + .map( repositoryRegistry::getManagedRepository ).collect( Collectors.toList( ) ) ); + } + + } + + @Override + public Collection<RepositoryGroup> getAll( ) + { return repositoryGroups.values( ); } - public boolean hasRepositoryGroup(String id) { + @Override + public boolean has( String id ) + { return repositoryGroups.containsKey( id ); } @PreDestroy - private void destroy() { + private void destroy( ) + { this.close( ); } - public void close() { - for (RepositoryGroup group : repositoryGroups.values()) { + @Override + public void close( ) + { + for ( RepositoryGroup group : repositoryGroups.values( ) ) + { try { mergedRemoteIndexesScheduler.unschedule( group ); group.close( ); - } catch (Throwable e) { + } + catch ( Throwable e ) + { log.error( "Could not close repository group {}: {}", group.getId( ), e.getMessage( ) ); } } - this.repositoryGroups.clear(); + this.repositoryGroups.clear( ); } } diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/validation/CommonGroupValidator.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/validation/CommonGroupValidator.java new file mode 100644 index 000000000..f9fd6fd1e --- /dev/null +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/validation/CommonGroupValidator.java @@ -0,0 +1,144 @@ +package org.apache.archiva.repository.base.validation; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.repository.Repository; +import org.apache.archiva.repository.RepositoryGroup; +import org.apache.archiva.repository.RepositoryRegistry; +import org.apache.archiva.repository.base.ConfigurationHandler; +import org.apache.archiva.repository.validation.AbstractRepositoryValidator; +import org.apache.archiva.repository.validation.RepositoryValidator; +import org.apache.archiva.repository.validation.ValidationError; +import org.apache.archiva.repository.validation.ValidationResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * A validator for repository groups. All validation errors are prefixed with category 'repository_group'. + * + * @author Martin Stockhammer <martin_s@apache.org> + */ +@Service( "repositoryValidator#common#group" ) +public class CommonGroupValidator extends AbstractRepositoryValidator<RepositoryGroup> implements RepositoryValidator<RepositoryGroup> +{ + + private static final Pattern REPO_GROUP_ID_PATTERN = Pattern.compile( "[A-Za-z0-9\\._\\-]+" ); + private final ConfigurationHandler configurationHandler; + + private RepositoryRegistry repositoryRegistry; + + public CommonGroupValidator( ConfigurationHandler configurationHandler ) + { + this.configurationHandler = configurationHandler; + } + + + private Map<String, List<ValidationError>> appendError( Map<String, List<ValidationError>> errorMap, String errorKey, Object... parameter ) + { + Map<String, List<ValidationError>> result; + result = errorMap == null ? new HashMap<>( ) : errorMap; + ValidationError error = ValidationError.ofKey( errorKey, parameter ); + List<ValidationError> errList = result.computeIfAbsent( error.getAttribute( ), k -> new ArrayList<ValidationError>( ) ); + errList.add( error ); + return result; + } + + public ValidationResponse apply( RepositoryGroup repositoryGroup, boolean updateMode ) throws IllegalArgumentException + { + final String repoGroupId = repositoryGroup.getId( ); + Map<String, List<ValidationError>> errors = null; + if ( StringUtils.isBlank( repoGroupId ) ) + { + errors = appendError( errors, "repository_group.id.empty" ); + } + + if ( repoGroupId.length( ) > 100 ) + { + errors = appendError( errors, "repository_group.id.max_length", repoGroupId, Integer.toString( 100 ) ); + + } + + Matcher matcher = REPO_GROUP_ID_PATTERN.matcher( repoGroupId ); + if ( !matcher.matches( ) ) + { + errors = appendError( errors, "repository_group.id.invalid_chars", "alphanumeric, '.', '-','_'" ); + } + + if ( repositoryGroup.getMergedIndexTTL( ) <= 0 ) + { + errors = appendError( errors, "repository_group.merged_index_ttl.min", "0" ); + } + + + if ( repositoryRegistry != null && !updateMode ) + { + if ( repositoryRegistry.hasRepositoryGroup( repoGroupId ) ) + { + errors = appendError( errors, "repository_group.id.group_exists", repoGroupId ); + } + else if ( repositoryRegistry.hasManagedRepository( repoGroupId ) ) + { + errors = appendError( errors, "repository_group.id.managed_exists" ); + } + else if ( repositoryRegistry.hasRemoteRepository( repoGroupId ) ) + { + errors = appendError( errors, "repository_group.id.remote_exists" ); + } + } + return new ValidationResponse(repositoryGroup, errors ); + } + + + + + public ConfigurationHandler getConfigurationHandler( ) + { + return configurationHandler; + } + + public RepositoryRegistry getRepositoryRegistry( ) + { + return repositoryRegistry; + } + + @Override + public void setRepositoryRegistry( RepositoryRegistry repositoryRegistry ) + { + this.repositoryRegistry = repositoryRegistry; + } + + @Override + public Class<RepositoryGroup> getFlavour( ) + { + return RepositoryGroup.class; + } + + @Override + public boolean isFlavour( Class<?> clazz ) + { + return RepositoryGroup.class.isAssignableFrom( clazz ); + } +} diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistryTest.java b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistryTest.java index b7cbf1fb5..00bbbc8c1 100644 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistryTest.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistryTest.java @@ -30,15 +30,14 @@ import org.apache.archiva.repository.Repository; import org.apache.archiva.repository.RepositoryException; import org.apache.archiva.repository.RepositoryRegistry; import org.apache.archiva.repository.RepositoryType; -import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.inject.Inject; import java.io.IOException; @@ -50,12 +49,13 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Collection; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; + /** * Test for RepositoryRegistry */ -@RunWith(ArchivaSpringJUnit4ClassRunner.class) +@ExtendWith( SpringExtension.class) @ContextConfiguration(locations = { "classpath*:/META-INF/spring-context.xml", "classpath:/spring-context.xml" }) public class ArchivaRepositoryRegistryTest { @@ -75,7 +75,7 @@ public class ArchivaRepositoryRegistryTest private static Path cfgCopy; private static Path archivaCfg; - @BeforeClass + @BeforeAll public static void classSetup() throws IOException, URISyntaxException { URL archivaCfgUri = Thread.currentThread().getContextClassLoader().getResource( "archiva.xml" ); @@ -86,7 +86,7 @@ public class ArchivaRepositoryRegistryTest } } - @AfterClass + @AfterAll public static void classTearDown() throws IOException { if (cfgCopy!=null) { @@ -94,7 +94,7 @@ public class ArchivaRepositoryRegistryTest } } - @Before + @BeforeEach public void setUp( ) throws Exception { assertNotNull( repositoryRegistry ); @@ -111,7 +111,7 @@ public class ArchivaRepositoryRegistryTest repositoryRegistry.reload(); } - @After + @AfterEach public void tearDown( ) throws Exception { Files.deleteIfExists( userCfg ); @@ -160,7 +160,7 @@ public class ArchivaRepositoryRegistryTest assertTrue(repo instanceof ManagedRepository); assertTrue( repo.hasIndex( ) ); assertTrue(repo.isScanned()); - Assert.assertEquals( RepositoryType.MAVEN, repo.getType()); + assertEquals( RepositoryType.MAVEN, repo.getType()); } @Test @@ -213,7 +213,7 @@ public class ArchivaRepositoryRegistryTest managedRepository = BasicManagedRepository.newFilesystemInstance("central", "Test repo", archivaConfiguration.getRepositoryBaseDir().resolve("central")); managedRepository.setDescription( managedRepository.getPrimaryLocale(), "This is just a test" ); - ManagedRepository updatedRepo = null; + ManagedRepository updatedRepo; try { repositoryRegistry.putRepository( managedRepository ); throw new RuntimeException("Repository exception should be thrown, if there exists a remote repository already with that id"); @@ -224,12 +224,12 @@ public class ArchivaRepositoryRegistryTest managedRepository.setDescription( managedRepository.getPrimaryLocale(), "This is just a test" ); updatedRepo = repositoryRegistry.putRepository( managedRepository ); - assertTrue(updatedRepo==managedRepository); + assertSame( updatedRepo, managedRepository ); assertNotNull(managedRepository.getContent()); assertEquals(6, repositoryRegistry.getRepositories().size()); ManagedRepository managedRepository1 = repositoryRegistry.getManagedRepository( "internal" ); assertEquals("Test repo", managedRepository1.getName()); - assertTrue(managedRepository1==managedRepository); + assertSame( managedRepository1, managedRepository ); } @@ -253,7 +253,7 @@ public class ArchivaRepositoryRegistryTest cfg.setId("internal"); cfg.setName("This is internal test 002"); repo = repositoryRegistry.putRepository( cfg ); - assertTrue(internalRepo==repo); + assertSame( internalRepo, repo ); assertEquals("This is internal test 002",repo.getName()); assertEquals(5, repositoryRegistry.getManagedRepositories().size()); @@ -284,7 +284,7 @@ public class ArchivaRepositoryRegistryTest cfg.setId("internal"); cfg.setName("This is internal test 002"); repo = repositoryRegistry.putRepository( cfg, configuration ); - assertTrue(internalRepo==repo); + assertSame( internalRepo, repo ); assertEquals("This is internal test 002",repo.getName()); assertEquals(5, repositoryRegistry.getManagedRepositories().size()); @@ -299,16 +299,17 @@ public class ArchivaRepositoryRegistryTest remoteRepository.setDescription( remoteRepository.getPrimaryLocale(), "This is just a test" ); RemoteRepository newRepo = repositoryRegistry.putRepository(remoteRepository); - assertTrue(remoteRepository==newRepo); + assertSame( remoteRepository, newRepo ); assertNotNull(remoteRepository.getContent()); assertEquals(6, repositoryRegistry.getRepositories().size()); remoteRepository = BasicRemoteRepository.newFilesystemInstance( "internal", "Test repo", archivaConfiguration.getRemoteRepositoryBaseDir() ); remoteRepository.setDescription( remoteRepository.getPrimaryLocale(), "This is just a test" ); - RemoteRepository updatedRepo = null; + RemoteRepository updatedRepo; try { updatedRepo = repositoryRegistry.putRepository( remoteRepository ); + assertSame( remoteRepository, updatedRepo ); throw new RuntimeException("Should throw repository exception, if repository exists already and is not the same type."); } catch (RepositoryException e) { // OK @@ -318,12 +319,12 @@ public class ArchivaRepositoryRegistryTest remoteRepository.setDescription( remoteRepository.getPrimaryLocale(), "This is just a test" ); updatedRepo = repositoryRegistry.putRepository( remoteRepository ); - assertTrue(updatedRepo==remoteRepository); + assertSame( updatedRepo, remoteRepository ); assertNotNull(remoteRepository.getContent()); assertEquals(6, repositoryRegistry.getRepositories().size()); RemoteRepository remoteRepository1 = repositoryRegistry.getRemoteRepository( "central" ); assertEquals("Test repo", remoteRepository1.getName()); - assertTrue(remoteRepository1==remoteRepository); + assertSame( remoteRepository1, remoteRepository ); } @Test @@ -346,7 +347,7 @@ public class ArchivaRepositoryRegistryTest cfg.setId("central"); cfg.setName("This is central test 002"); repo = repositoryRegistry.putRepository( cfg ); - assertTrue(internalRepo==repo); + assertSame( internalRepo, repo ); assertEquals("This is central test 002",repo.getName()); assertEquals(2, repositoryRegistry.getRemoteRepositories().size()); @@ -376,7 +377,7 @@ public class ArchivaRepositoryRegistryTest cfg.setId("central"); cfg.setName("This is central test 002"); repo = repositoryRegistry.putRepository( cfg, configuration ); - assertTrue(internalRepo==repo); + assertSame( internalRepo, repo ); assertEquals("This is central test 002",repo.getName()); assertEquals(2, repositoryRegistry.getRemoteRepositories().size()); @@ -483,7 +484,7 @@ public class ArchivaRepositoryRegistryTest assertNotNull(clone); assertNull(clone.getContent()); assertEquals("Archiva Managed Internal Repository", clone.getName()); - assertFalse(managedRepository==clone); + assertNotSame( managedRepository, clone ); } @@ -516,8 +517,15 @@ public class ArchivaRepositoryRegistryTest assertNotNull(clone); assertNull(clone.getContent()); assertEquals("Central Repository", clone.getName()); - assertFalse(remoteRepository==clone); + assertNotSame( remoteRepository, clone ); + + } + @Test + void validateRepository() { + Repository repo = repositoryRegistry.getRepository( "internal" ); + assertNotNull( repo ); + assertTrue( repositoryRegistry.validateRepository( repo ).isValid() ); } }
\ No newline at end of file |