import org.apache.archiva.configuration.RepositoryGroupConfiguration;
import org.apache.archiva.metadata.model.facets.AuditEvent;
import org.apache.archiva.indexer.merger.MergedRemoteIndexesScheduler;
+import org.apache.archiva.repository.EditableRepository;
+import org.apache.archiva.repository.EditableRepositoryGroup;
+import org.apache.archiva.repository.RepositoryException;
import org.apache.archiva.repository.RepositoryRegistry;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.commons.lang.StringUtils;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
+import javax.inject.Named;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
private ManagedRepositoryAdmin managedRepositoryAdmin;
@Inject
+ @Named("mergedRemoteIndexesScheduler#default")
private MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler;
@Inject
}
@Override
- public List<RepositoryGroup> getRepositoriesGroups()
- throws RepositoryAdminException
- {
+ public List<RepositoryGroup> getRepositoriesGroups() {
return repositoryRegistry.getRepositoryGroups().stream().map( r -> convertRepositoryGroupObject( r ) ).collect( Collectors.toList());
}
@Override
- public RepositoryGroup getRepositoryGroup( String repositoryGroupId )
- throws RepositoryAdminException
- {
+ public RepositoryGroup getRepositoryGroup( String repositoryGroupId ) {
return convertRepositoryGroupObject( repositoryRegistry.getRepositoryGroup( repositoryGroupId ) );
}
repositoryGroupConfiguration.setRepositories( repositoryGroup.getRepositories() );
repositoryGroupConfiguration.setMergedIndexPath( repositoryGroup.getMergedIndexPath() );
repositoryGroupConfiguration.setMergedIndexTtl( repositoryGroup.getMergedIndexTtl() );
- repositoryGroupConfiguration.setCronExpression( repositoryGroup.getCronExpression() );
- Configuration configuration = getArchivaConfiguration().getConfiguration();
- configuration.addRepositoryGroup( repositoryGroupConfiguration );
- saveConfiguration( configuration );
+ repositoryGroupConfiguration.setCronExpression( StringUtils.isEmpty(repositoryGroup.getCronExpression()) ? "0 0 03 ? * MON" : repositoryGroup.getCronExpression() );
+
+ try {
+ repositoryRegistry.putRepositoryGroup(repositoryGroupConfiguration);
+ } catch (RepositoryException e) {
+ e.printStackTrace();
+ }
+
triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.ADD_REPO_GROUP, auditInformation );
mergedRemoteIndexesScheduler.schedule( repositoryRegistry.getRepositoryGroup( repositoryGroup.getId()), getMergedIndexDirectory( repositoryGroup.getId() ) );
return Boolean.TRUE;
public Boolean deleteRepositoryGroup( String repositoryGroupId, AuditInformation auditInformation )
throws RepositoryAdminException
{
- Configuration configuration = getArchivaConfiguration().getConfiguration();
- RepositoryGroupConfiguration repositoryGroupConfiguration =
- configuration.getRepositoryGroupsAsMap().get( repositoryGroupId );
+
+ org.apache.archiva.repository.RepositoryGroup repositoryGroup = repositoryRegistry.getRepositoryGroup(repositoryGroupId);
+ try {
+ repositoryRegistry.removeRepositoryGroup(repositoryGroup);
+ } catch (RepositoryException e) {
+ log.error("Removal of repository group {} failed: {}", repositoryGroup.getId(), e.getMessage(), e);
+ throw new RepositoryAdminException("Removal of repository failed: " + e.getMessage(), e);
+ }
mergedRemoteIndexesScheduler.unschedule(
repositoryRegistry.getRepositoryGroup( repositoryGroupId ) );
- if ( repositoryGroupConfiguration == null )
- {
- throw new RepositoryAdminException(
- "repositoryGroup with id " + repositoryGroupId + " doesn't not exists so cannot remove" );
- }
- configuration.removeRepositoryGroup( repositoryGroupConfiguration );
triggerAuditEvent( repositoryGroupId, null, AuditEvent.DELETE_REPO_GROUP, auditInformation );
return Boolean.TRUE;
{
validateRepositoryGroup( repositoryGroup, true );
validateManagedRepositoriesExists( repositoryGroup.getRepositories() );
+
+
Configuration configuration = getArchivaConfiguration().getConfiguration();
RepositoryGroupConfiguration repositoryGroupConfiguration =
configuration.getRepositoryGroupsAsMap().get( repositoryGroup.getId() );
- configuration.removeRepositoryGroup( repositoryGroupConfiguration );
-
repositoryGroupConfiguration.setRepositories( repositoryGroup.getRepositories() );
repositoryGroupConfiguration.setMergedIndexPath( repositoryGroup.getMergedIndexPath() );
repositoryGroupConfiguration.setMergedIndexTtl( repositoryGroup.getMergedIndexTtl() );
repositoryGroupConfiguration.setCronExpression( repositoryGroup.getCronExpression() );
- configuration.addRepositoryGroup( repositoryGroupConfiguration );
-
- saveConfiguration( configuration );
- if ( triggerAuditEvent )
- {
- triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.MODIFY_REPO_GROUP, auditInformation );
+ try {
+ repositoryRegistry.putRepositoryGroup(repositoryGroupConfiguration);
+ } catch (RepositoryException e) {
+ e.printStackTrace();
}
+
org.apache.archiva.repository.RepositoryGroup rg = repositoryRegistry.getRepositoryGroup( repositoryGroup.getId( ) );
mergedRemoteIndexesScheduler.unschedule( rg );
mergedRemoteIndexesScheduler.schedule( rg, getMergedIndexDirectory( repositoryGroup.getId() ) );
AuditInformation auditInformation )
throws RepositoryAdminException
{
- RepositoryGroup repositoryGroup = getRepositoryGroup( repositoryGroupId );
+ org.apache.archiva.repository.RepositoryGroup repositoryGroup = repositoryRegistry.getRepositoryGroup( repositoryGroupId );
if ( repositoryGroup == null )
{
throw new RepositoryAdminException(
- "repositoryGroup with id " + repositoryGroupId + " doesn't not exists so cannot add repository to it" );
+ "repositoryGroup with id " + repositoryGroupId + " doesn't not exists so cannot add repository to it" );
}
- if ( repositoryGroup.getRepositories().contains( repositoryId ) )
+ if (!(repositoryGroup instanceof EditableRepositoryGroup)) {
+ throw new RepositoryAdminException("The repository group is not editable "+repositoryGroupId);
+ }
+ EditableRepositoryGroup editableRepositoryGroup = (EditableRepositoryGroup) repositoryGroup;
+ if ( editableRepositoryGroup.getRepositories().stream().anyMatch( repo -> repositoryId.equals(repo.getId())) )
{
throw new RepositoryAdminException(
"repositoryGroup with id " + repositoryGroupId + " already contain repository with id" + repositoryId );
}
- validateManagedRepositoriesExists( Arrays.asList( repositoryId ) );
+ org.apache.archiva.repository.ManagedRepository managedRepo = repositoryRegistry.getManagedRepository(repositoryId);
+ if (managedRepo==null) {
+ throw new RepositoryAdminException("Repository with id "+repositoryId+" does not exist" );
+ }
- repositoryGroup.addRepository( repositoryId );
- updateRepositoryGroup( repositoryGroup, auditInformation, false );
+ editableRepositoryGroup.addRepository( managedRepo );
+ try {
+ repositoryRegistry.putRepositoryGroup(editableRepositoryGroup);
+ } catch (RepositoryException e) {
+ throw new RepositoryAdminException("Could not store the repository group "+repositoryGroupId, e);
+ }
triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.ADD_REPO_TO_GROUP, auditInformation );
return Boolean.TRUE;
}
AuditInformation auditInformation )
throws RepositoryAdminException
{
- RepositoryGroup repositoryGroup = getRepositoryGroup( repositoryGroupId );
+ org.apache.archiva.repository.RepositoryGroup repositoryGroup = repositoryRegistry.getRepositoryGroup( repositoryGroupId );
if ( repositoryGroup == null )
{
throw new RepositoryAdminException( "repositoryGroup with id " + repositoryGroupId
+ " doesn't not exists so cannot remove repository from it" );
}
- if ( !repositoryGroup.getRepositories().contains( repositoryId ) )
+ if ( !repositoryGroup.getRepositories().stream().anyMatch( repo -> repositoryId.equals(repo.getId()) ) )
{
throw new RepositoryAdminException(
"repositoryGroup with id " + repositoryGroupId + " doesn't not contains repository with id"
+ repositoryId
);
}
+ if (!(repositoryGroup instanceof EditableRepositoryGroup)) {
+ throw new RepositoryAdminException("Repository group is not editable " + repositoryGroupId);
+ }
+ EditableRepositoryGroup editableRepositoryGroup = (EditableRepositoryGroup) repositoryGroup;
- repositoryGroup.removeRepository( repositoryId );
- updateRepositoryGroup( repositoryGroup, auditInformation, false );
+ editableRepositoryGroup.removeRepository( repositoryId );
+ try {
+ repositoryRegistry.putRepositoryGroup(editableRepositoryGroup);
+ } catch (RepositoryException e) {
+ throw new RepositoryAdminException("Could not store repository group " + repositoryGroupId, e);
+ }
triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.DELETE_REPO_FROM_GROUP, auditInformation );
return Boolean.TRUE;
}
}
}
+ @Override
+ public ArchivaIndexingContext mergeContexts(Repository destinationRepo, List<ArchivaIndexingContext> contexts, boolean packIndex) throws UnsupportedOperationException, IndexCreationFailedException {
+ return null;
+ }
+
private StorageAsset getIndexPath( Repository repo) throws IOException {
IndexCreationFeature icf = repo.getFeature(IndexCreationFeature.class).get();
Path repoDir = repo.getLocalPath();
import org.apache.archiva.repository.Repository;
import org.apache.archiva.repository.RepositoryType;
+import org.apache.archiva.repository.content.StorageAsset;
import java.net.URI;
+import java.nio.file.Path;
import java.util.Collection;
+import java.util.List;
public interface ArchivaIndexManager {
* Updates the local path where the index is stored using the repository information.
* @return
*/
- public void updateLocalIndexPath(Repository repo);
+ void updateLocalIndexPath(Repository repo);
+
+
+ /**
+ * Merges a list of contexts into a single one.
+ *
+ * @param destinationRepo The destination repository
+ * @param contexts The contexts of the indexes that should be merged.
+ * @param packIndex True, if the merged index should be packed, otherwise false.
+ * @return The merged context
+ * @throws UnsupportedOperationException if the underlying implementation does not allow to merge indexing contexts
+ */
+ ArchivaIndexingContext mergeContexts(Repository destinationRepo, List<ArchivaIndexingContext> contexts,
+ boolean packIndex) throws UnsupportedOperationException,
+ IndexCreationFailedException;
}
+++ /dev/null
-package org.apache.archiva.indexer.merger;
-
-/*
- * 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.common.utils.FileUtils;
-import org.apache.archiva.indexer.ArchivaIndexingContext;
-import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.commons.lang.time.StopWatch;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.scheduling.annotation.Async;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.stream.Collectors;
-
-/**
- * @author Martin Stockhammer <martin_s@apache.org>
- */
-public class BasicIndexMerger implements IndexMerger
-{
- @Inject
- RepositoryRegistry repositoryRegistry;
-
- private Logger log = LoggerFactory.getLogger( getClass() );
-
-
- private List<TemporaryGroupIndex> temporaryGroupIndexes = new CopyOnWriteArrayList<>();
-
- private List<String> runningGroups = new CopyOnWriteArrayList<>();
-
- @Inject
- public BasicIndexMerger( )
- {
- }
-
- @Override
- public ArchivaIndexingContext buildMergedIndex( IndexMergerRequest indexMergerRequest )
- throws IndexMergerException
- {
- String groupId = indexMergerRequest.getGroupId();
-
- if ( runningGroups.contains( groupId ) )
- {
- log.info( "skip build merge remote indexes for id: '{}' as already running", groupId );
- return null;
- }
-
- runningGroups.add( groupId );
-
- StopWatch stopWatch = new StopWatch();
- stopWatch.reset();
- stopWatch.start();
-
- Path mergedIndexDirectory = indexMergerRequest.getMergedIndexDirectory();
-
- String tempRepoId = mergedIndexDirectory.getFileName().toString();
-
- try
- {
- Path indexLocation = mergedIndexDirectory.resolve( indexMergerRequest.getMergedIndexPath() );
-
- List<ArchivaIndexingContext> members = indexMergerRequest.getRepositoriesIds( ).stream( ).map( id ->
- repositoryRegistry.getRepository( id ) )
- .map( repo -> repo.getIndexingContext() ).filter( Objects::nonNull ).collect( Collectors.toList() );
-
- members.get( 0 ).
- if ( indexMergerRequest.isPackIndex() )
- {
- IndexPackingRequest request = new IndexPackingRequest( mergedCtx, //
- mergedCtx.acquireIndexSearcher().getIndexReader(), //
- indexLocation.toFile() );
- indexPacker.packIndex( request );
- }
-
- if ( indexMergerRequest.isTemporary() )
- {
- temporaryGroupIndexes.add( new TemporaryGroupIndex( mergedIndexDirectory, tempRepoId, groupId,
- indexMergerRequest.getMergedIndexTtl() ) );
- }
- stopWatch.stop();
- log.info( "merged index for repos {} in {} s", indexMergerRequest.getRepositoriesIds(),
- stopWatch.getTime() );
- return new MavenIndexContext(repositoryRegistry.getRepositoryGroup(groupId), mergedCtx);
- }
- catch ( IOException e)
- {
- throw new IndexMergerException( e.getMessage(), e );
- }
- finally
- {
- runningGroups.remove( groupId );
- }
- }
-
- @Async
- @Override
- public void cleanTemporaryGroupIndex( TemporaryGroupIndex temporaryGroupIndex )
- {
- if ( temporaryGroupIndex == null )
- {
- return;
- }
-
- try
- {
-
- Optional<IndexingContext> ctxOpt = temporaryContextes.stream( ).filter( ctx -> ctx.getId( ).equals( temporaryGroupIndex.getIndexId( ) ) ).findFirst( );
- if (ctxOpt.isPresent()) {
- IndexingContext ctx = ctxOpt.get();
- indexer.closeIndexingContext( ctx, true );
- temporaryGroupIndexes.remove( temporaryGroupIndex );
- temporaryContextes.remove( ctx );
- Path directory = temporaryGroupIndex.getDirectory();
- if ( directory != null && Files.exists(directory) )
- {
- FileUtils.deleteDirectory( directory );
- }
- }
- }
- catch ( IOException e )
- {
- log.warn( "fail to delete temporary group index {}", temporaryGroupIndex.getIndexId(), e );
- }
- }
-
- @Override
- public Collection<TemporaryGroupIndex> getTemporaryGroupIndexes()
- {
- return this.temporaryGroupIndexes;
- }
-
-}
--- /dev/null
+package org.apache.archiva.indexer.merger;
+/*
+ * 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.common.utils.FileUtils;
+import org.apache.archiva.indexer.ArchivaIndexManager;
+import org.apache.archiva.indexer.ArchivaIndexingContext;
+import org.apache.archiva.indexer.IndexCreationFailedException;
+import org.apache.archiva.indexer.merger.IndexMerger;
+import org.apache.archiva.indexer.merger.IndexMergerException;
+import org.apache.archiva.indexer.merger.IndexMergerRequest;
+import org.apache.archiva.indexer.merger.TemporaryGroupIndex;
+import org.apache.archiva.repository.Repository;
+import org.apache.archiva.repository.RepositoryRegistry;
+import org.apache.commons.lang.time.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
+
+/**
+ * @author Olivier Lamy
+ * @since 1.4-M2
+ */
+@Service("indexMerger#default")
+public class DefaultIndexMerger
+ implements IndexMerger
+{
+
+ @Inject
+ RepositoryRegistry repositoryRegistry;
+
+ private Logger log = LoggerFactory.getLogger( getClass() );
+
+ private List<TemporaryGroupIndex> temporaryGroupIndexes = new CopyOnWriteArrayList<>();
+
+ private List<ArchivaIndexingContext> temporaryContextes = new CopyOnWriteArrayList<>( );
+
+ private List<String> runningGroups = new CopyOnWriteArrayList<>();
+
+ @Inject
+ public DefaultIndexMerger( )
+ {
+ }
+
+ @Override
+ public ArchivaIndexingContext buildMergedIndex(IndexMergerRequest indexMergerRequest )
+ throws IndexMergerException
+ {
+ String groupId = indexMergerRequest.getGroupId();
+
+ if ( runningGroups.contains( groupId ) )
+ {
+ log.info( "skip build merge remote indexes for id: '{}' as already running", groupId );
+ return null;
+ }
+
+ runningGroups.add( groupId );
+ StopWatch stopWatch = new StopWatch();
+ try {
+ stopWatch.reset();
+ stopWatch.start();
+
+ Path mergedIndexDirectory = indexMergerRequest.getMergedIndexDirectory();
+ Repository destinationRepository = repositoryRegistry.getRepository(indexMergerRequest.getGroupId());
+
+ ArchivaIndexManager idxManager = repositoryRegistry.getIndexManager(destinationRepository.getType());
+ List<ArchivaIndexingContext> sourceContexts = indexMergerRequest.getRepositoriesIds().stream().map(id -> repositoryRegistry.getRepository(id).getIndexingContext()).collect(Collectors.toList());
+ try {
+ ArchivaIndexingContext result = idxManager.mergeContexts(destinationRepository, sourceContexts, indexMergerRequest.isPackIndex());
+ if ( indexMergerRequest.isTemporary() )
+ {
+ String tempRepoId = destinationRepository.getId()+System.currentTimeMillis();
+ temporaryGroupIndexes.add( new TemporaryGroupIndex( mergedIndexDirectory, tempRepoId, groupId,
+ indexMergerRequest.getMergedIndexTtl() ) );
+ temporaryContextes.add(result);
+ }
+ return result;
+ } catch (IndexCreationFailedException e) {
+ throw new IndexMergerException("Index merging failed " + e.getMessage(), e);
+ }
+
+ } finally {
+ stopWatch.stop();
+ log.info( "merged index for repos {} in {} s", indexMergerRequest.getRepositoriesIds(),
+ stopWatch.getTime() );
+ runningGroups.remove(groupId);
+ }
+ }
+
+ @Async
+ @Override
+ public void cleanTemporaryGroupIndex( TemporaryGroupIndex temporaryGroupIndex )
+ {
+ if ( temporaryGroupIndex == null )
+ {
+ return;
+ }
+
+ try
+ {
+ Optional<ArchivaIndexingContext> ctxOpt = temporaryContextes.stream( ).filter( ctx -> ctx.getId( ).equals( temporaryGroupIndex.getIndexId( ) ) ).findFirst( );
+ if (ctxOpt.isPresent()) {
+ ArchivaIndexingContext ctx = ctxOpt.get();
+ ctx.close(true);
+ temporaryGroupIndexes.remove( temporaryGroupIndex );
+ temporaryContextes.remove( ctx );
+ Path directory = temporaryGroupIndex.getDirectory();
+ if ( directory != null && Files.exists(directory) )
+ {
+ FileUtils.deleteDirectory( directory );
+ }
+ }
+ }
+ catch ( IOException e )
+ {
+ log.warn( "fail to delete temporary group index {}", temporaryGroupIndex.getIndexId(), e );
+ }
+ }
+
+ @Override
+ public Collection<TemporaryGroupIndex> getTemporaryGroupIndexes()
+ {
+ return this.temporaryGroupIndexes;
+ }
+}
* The modification methods addXX and removeXX persist the changes immediately to the configuration. If the
* configuration save fails the changes are rolled back.
*
- * TODO: Audit events should be sent, but we don't want dependency to the repsitory-metadata-api
+ * TODO: Audit events
*/
@Service( "repositoryRegistry" )
public class RepositoryRegistry implements ConfigurationListener, RepositoryEventHandler, RepositoryEventListener {
}
- private ArchivaIndexManager getIndexManager(RepositoryType type) {
+ public ArchivaIndexManager getIndexManager(RepositoryType type) {
return indexManagerFactory.getIndexManager(type);
}
log.debug("Managed repo");
return managedRepositories.get( repoId );
}
- else
+ else if (remoteRepositories.containsKey(repoId))
{
log.debug("Remote repo");
return remoteRepositories.get( repoId );
+ } else if (repositoryGroups.containsKey(repoId)) {
+ return repositoryGroups.get(repoId);
+ } else {
+ return null;
}
}
finally
}
}
+
+ /**
+ * Adds a new repository group to the current list, or replaces the repository group definition with
+ * the same id, if it exists already.
+ * The change is saved to the configuration immediately.
+ *
+ * @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
+ {
+ rwLock.writeLock( ).lock( );
+ try
+ {
+ final String id = repositoryGroup.getId();
+ RepositoryGroup originRepo = repositoryGroups.put( id, repositoryGroup );
+ try
+ {
+ if (originRepo!=null) {
+ originRepo.close();
+ }
+ RepositoryProvider provider = getProvider( repositoryGroup.getType() );
+ RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration( repositoryGroup );
+ Configuration configuration = getArchivaConfiguration( ).getConfiguration( );
+ updateRepositoryReferences( provider, repositoryGroup, newCfg );
+ RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( id );
+ if (oldCfg!=null) {
+ configuration.removeRepositoryGroup( oldCfg );
+ }
+ configuration.addRepositoryGroup( newCfg );
+ getArchivaConfiguration( ).save( configuration );
+ return repositoryGroup;
+ }
+ catch ( Exception e )
+ {
+ // Rollback
+ if ( originRepo != null )
+ {
+ repositoryGroups.put( id, originRepo );
+ } 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
+ {
+ rwLock.writeLock( ).unlock( );
+ }
+ }
+
+ /**
+ * Adds a new repository group or updates the repository with the same id, if it exists already.
+ * The configuration is saved immediately.
+ *
+ * @param repositoryGroupConfiguration the repository configuration
+ * @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
+ {
+ rwLock.writeLock( ).lock( );
+ try
+ {
+ final String id = repositoryGroupConfiguration.getId();
+ final RepositoryType repositoryType = RepositoryType.valueOf( repositoryGroupConfiguration.getType() );
+ Configuration configuration = getArchivaConfiguration().getConfiguration();
+ RepositoryGroup repo = repositoryGroups.get(id);
+ RepositoryGroupConfiguration oldCfg = repo!=null ? getProvider( repositoryType ).getRepositoryGroupConfiguration( repo ) : null;
+ repo = putRepositoryGroup( repositoryGroupConfiguration, configuration );
+ try
+ {
+ getArchivaConfiguration().save(configuration);
+ }
+ catch ( IndeterminateConfigurationException | RegistryException e )
+ {
+ if (oldCfg!=null) {
+ getProvider( repositoryType ).updateRepositoryGroupInstance( (EditableRepositoryGroup) repo, oldCfg );
+ }
+ 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() );
+ }
+ return repo;
+ }
+ 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.
+ *
+ * @param repositoryGroupConfiguration the new or changed repository configuration
+ * @param configuration the configuration object
+ * @return the new or updated repository
+ * @throws RepositoryException if the configuration cannot be saved or updated
+ */
+ @SuppressWarnings( "unchecked" )
+ public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) throws RepositoryException
+ {
+ rwLock.writeLock( ).lock( );
+ try
+ {
+ final String id = repositoryGroupConfiguration.getId();
+ final RepositoryType repoType = RepositoryType.valueOf( repositoryGroupConfiguration.getType() );
+ RepositoryGroup repo;
+ setRepositoryGroupDefaults(repositoryGroupConfiguration);
+ if (repositoryGroups.containsKey( id )) {
+ repo = repositoryGroups.get(id);
+ if (repo instanceof EditableRepositoryGroup)
+ {
+ getProvider( repoType ).updateRepositoryGroupInstance( (EditableRepositoryGroup) repo, repositoryGroupConfiguration );
+ } else {
+ throw new RepositoryException( "The repository is not editable "+id );
+ }
+ } else
+ {
+ repo = getProvider( repoType ).createRepositoryGroup( repositoryGroupConfiguration );
+ repo.addListener(this);
+ repositoryGroups.put(id, repo);
+ }
+ updateRepositoryReferences( getProvider( repoType ), repo, repositoryGroupConfiguration );
+ replaceOrAddRepositoryConfig( repositoryGroupConfiguration, configuration );
+ return repo;
+ }
+ finally
+ {
+ rwLock.writeLock( ).unlock( );
+ }
+ }
+
+ private void setRepositoryGroupDefaults(RepositoryGroupConfiguration repositoryGroupConfiguration) {
+ if (StringUtils.isEmpty(repositoryGroupConfiguration.getMergedIndexPath())) {
+ repositoryGroupConfiguration.setMergedIndexPath(".indexer");
+ }
+ if (repositoryGroupConfiguration.getMergedIndexTtl()<=0) {
+ repositoryGroupConfiguration.setMergedIndexTtl(300);
+ }
+ if (StringUtils.isEmpty(repositoryGroupConfiguration.getCronExpression())) {
+ repositoryGroupConfiguration.setCronExpression("0 0 03 ? * MON");
+ }
+ }
+
private void replaceOrAddRepositoryConfig(ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration) {
ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById( managedRepositoryConfiguration.getId() );
if ( oldCfg !=null) {
configuration.addRemoteRepository( remoteRepositoryConfiguration );
}
+ private void replaceOrAddRepositoryConfig(RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) {
+ RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( repositoryGroupConfiguration.getId() );
+ if ( oldCfg !=null) {
+ configuration.removeRepositoryGroup( oldCfg );
+ }
+ configuration.addRepositoryGroup( repositoryGroupConfiguration);
+ }
+
public RemoteRepository putRepository( RemoteRepository remoteRepository, Configuration configuration) throws RepositoryException
{
rwLock.writeLock( ).lock( );
removeRepository( (RemoteRepository)repo );
} else if (repo instanceof ManagedRepository) {
removeRepository( (ManagedRepository)repo);
- } else {
+ } else if (repo instanceof RepositoryGroup ) {
+ removeRepositoryGroup((RepositoryGroup) repo);
+ }else {
throw new RepositoryException( "Repository type not known: "+repo.getClass() );
}
}
}
+ /**
+ * Removes a repository group from the registry and configuration, if it exists.
+ * The change is saved to the configuration immediately.
+ *
+ * @param repositoryGroup the repository group to remove
+ * @throws RepositoryException if a error occurs during configuration save
+ */
+ public void removeRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException
+ {
+ final String id = repositoryGroup.getId();
+ RepositoryGroup repo = getRepositoryGroup( id );
+ if (repo!=null) {
+ rwLock.writeLock().lock();
+ try {
+ repo = repositoryGroups.remove( id );
+ if (repo!=null) {
+ repo.close();
+ Configuration configuration = getArchivaConfiguration().getConfiguration();
+ RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById( id );
+ if (cfg!=null) {
+ configuration.removeRepositoryGroup( cfg );
+ }
+ getArchivaConfiguration().save( configuration );
+ }
+
+ }
+ 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() );
+ } finally
+ {
+ rwLock.writeLock().unlock();
+ }
+ }
+ }
+
+ public void removeRepositoryGroup(RepositoryGroup repositoryGroup, Configuration configuration) throws RepositoryException
+ {
+ final String id = repositoryGroup.getId();
+ RepositoryGroup repo = getRepositoryGroup( id );
+ if (repo!=null) {
+ rwLock.writeLock().lock();
+ try {
+ repo = repositoryGroups.remove( id );
+ if (repo!=null) {
+ repo.close();
+ RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById( id );
+ if (cfg!=null) {
+ configuration.removeRepositoryGroup( cfg );
+ }
+ }
+ } finally
+ {
+ rwLock.writeLock().unlock();
+ }
+ }
+
+ }
+
private void doRemoveRepo(RemoteRepository repo, Configuration configuration) {
repo.close();
RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById(repo.getId());
default-lazy-init="true">
<context:annotation-config/>
- <context:component-scan base-package="org.apache.archiva.repository,org.apache.archiva.repository.content"/>
+ <context:component-scan base-package="org.apache.archiva.repository,org.apache.archiva.repository.content,org.apache.archiva.indexer.merger"/>
+
+ <bean name="taskScheduler#mergeRemoteIndexes"
+ class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
+ <property name="poolSize" value="4"/>
+ <property name="threadGroupName" value="mergeRemoteIndexes"/>
+ </bean>
</beans>
\ No newline at end of file
import java.net.URI;
import java.util.Collection;
+import java.util.List;
/**
* @author Martin Stockhammer <martin_s@apache.org>
public void updateLocalIndexPath(Repository repo) {
}
+
+ @Override
+ public ArchivaIndexingContext mergeContexts(Repository destinationRepo, List<ArchivaIndexingContext> contexts, boolean packIndex) throws UnsupportedOperationException, IndexCreationFailedException {
+ return null;
+ }
}
+++ /dev/null
-package org.apache.archiva.indexer.maven;
-/*
- * 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.common.utils.FileUtils;
-import org.apache.archiva.indexer.ArchivaIndexingContext;
-import org.apache.archiva.indexer.UnsupportedBaseContextException;
-import org.apache.archiva.indexer.merger.IndexMerger;
-import org.apache.archiva.indexer.merger.IndexMergerException;
-import org.apache.archiva.indexer.merger.IndexMergerRequest;
-import org.apache.archiva.indexer.merger.TemporaryGroupIndex;
-import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.RepositoryType;
-import org.apache.commons.lang.time.StopWatch;
-import org.apache.maven.index.Indexer;
-import org.apache.maven.index.context.ContextMemberProvider;
-import org.apache.maven.index.context.IndexCreator;
-import org.apache.maven.index.context.IndexingContext;
-import org.apache.maven.index.context.StaticContextMemberProvider;
-import org.apache.maven.index.packer.IndexPacker;
-import org.apache.maven.index.packer.IndexPackingRequest;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.stream.Collectors;
-
-/**
- * @author Olivier Lamy
- * @since 1.4-M2
- */
-@Service("indexMerger#default")
-public class DefaultIndexMerger
- implements IndexMerger
-{
-
- @Inject
- RepositoryRegistry repositoryRegistry;
-
- private Logger log = LoggerFactory.getLogger( getClass() );
-
-
- private final IndexPacker indexPacker;
-
- private Indexer indexer;
-
- private final List<IndexCreator> indexCreators;
-
- private List<TemporaryGroupIndex> temporaryGroupIndexes = new CopyOnWriteArrayList<>();
-
- private List<IndexingContext> temporaryContextes = new CopyOnWriteArrayList<>( );
-
- private List<String> runningGroups = new CopyOnWriteArrayList<>();
-
- @Inject
- public DefaultIndexMerger( Indexer indexer, IndexPacker indexPacker, List<IndexCreator> indexCreators )
- {
- this.indexer = indexer;
- this.indexPacker = indexPacker;
- this.indexCreators = indexCreators;
- }
-
- @Override
- public ArchivaIndexingContext buildMergedIndex(IndexMergerRequest indexMergerRequest )
- throws IndexMergerException
- {
- String groupId = indexMergerRequest.getGroupId();
-
- if ( runningGroups.contains( groupId ) )
- {
- log.info( "skip build merge remote indexes for id: '{}' as already running", groupId );
- return null;
- }
-
- runningGroups.add( groupId );
-
- StopWatch stopWatch = new StopWatch();
- stopWatch.reset();
- stopWatch.start();
-
- Path mergedIndexDirectory = indexMergerRequest.getMergedIndexDirectory();
-
- String tempRepoId = mergedIndexDirectory.getFileName().toString();
-
- try
- {
- Path indexLocation = mergedIndexDirectory.resolve( indexMergerRequest.getMergedIndexPath() );
-
- List<IndexingContext> members = indexMergerRequest.getRepositoriesIds( ).stream( ).map( id ->
- repositoryRegistry.getRepository( id ) ).filter( repo -> repo.getType().equals( RepositoryType.MAVEN ) )
- .map( repo -> {
- try
- {
- return repo.getIndexingContext().getBaseContext( IndexingContext.class );
- }
- catch ( UnsupportedBaseContextException e )
- {
- return null;
- // Ignore
- }
- } ).filter( Objects::nonNull ).collect( Collectors.toList() );
- ContextMemberProvider memberProvider = new StaticContextMemberProvider(members);
- IndexingContext mergedCtx = indexer.createMergedIndexingContext( tempRepoId, tempRepoId, mergedIndexDirectory.toFile(),
- indexLocation.toFile(), true, memberProvider);
- mergedCtx.optimize();
-
- if ( indexMergerRequest.isPackIndex() )
- {
- IndexPackingRequest request = new IndexPackingRequest( mergedCtx, //
- mergedCtx.acquireIndexSearcher().getIndexReader(), //
- indexLocation.toFile() );
- indexPacker.packIndex( request );
- }
-
- if ( indexMergerRequest.isTemporary() )
- {
- temporaryGroupIndexes.add( new TemporaryGroupIndex( mergedIndexDirectory, tempRepoId, groupId,
- indexMergerRequest.getMergedIndexTtl() ) );
- temporaryContextes.add(mergedCtx);
- }
- stopWatch.stop();
- log.info( "merged index for repos {} in {} s", indexMergerRequest.getRepositoriesIds(),
- stopWatch.getTime() );
- return new MavenIndexContext(repositoryRegistry.getRepositoryGroup(groupId), mergedCtx);
- }
- catch ( IOException e)
- {
- throw new IndexMergerException( e.getMessage(), e );
- }
- finally
- {
- runningGroups.remove( groupId );
- }
- }
-
- @Async
- @Override
- public void cleanTemporaryGroupIndex( TemporaryGroupIndex temporaryGroupIndex )
- {
- if ( temporaryGroupIndex == null )
- {
- return;
- }
-
- try
- {
-
- Optional<IndexingContext> ctxOpt = temporaryContextes.stream( ).filter( ctx -> ctx.getId( ).equals( temporaryGroupIndex.getIndexId( ) ) ).findFirst( );
- if (ctxOpt.isPresent()) {
- IndexingContext ctx = ctxOpt.get();
- indexer.closeIndexingContext( ctx, true );
- temporaryGroupIndexes.remove( temporaryGroupIndex );
- temporaryContextes.remove( ctx );
- Path directory = temporaryGroupIndex.getDirectory();
- if ( directory != null && Files.exists(directory) )
- {
- FileUtils.deleteDirectory( directory );
- }
- }
- }
- catch ( IOException e )
- {
- log.warn( "fail to delete temporary group index {}", temporaryGroupIndex.getIndexId(), e );
- }
- }
-
- @Override
- public Collection<TemporaryGroupIndex> getTemporaryGroupIndexes()
- {
- return this.temporaryGroupIndexes;
- }
-}
import org.apache.archiva.indexer.IndexCreationFailedException;
import org.apache.archiva.indexer.IndexUpdateFailedException;
import org.apache.archiva.indexer.UnsupportedBaseContextException;
+import org.apache.archiva.indexer.merger.IndexMergerException;
+import org.apache.archiva.indexer.merger.TemporaryGroupIndex;
import org.apache.archiva.proxy.ProxyRegistry;
import org.apache.archiva.proxy.maven.WagonFactory;
import org.apache.archiva.proxy.maven.WagonFactoryException;
import org.apache.maven.index.Scanner;
import org.apache.maven.index.ScanningRequest;
import org.apache.maven.index.ScanningResult;
+import org.apache.maven.index.context.ContextMemberProvider;
import org.apache.maven.index.context.IndexCreator;
import org.apache.maven.index.context.IndexingContext;
+import org.apache.maven.index.context.StaticContextMemberProvider;
import org.apache.maven.index.packer.IndexPacker;
import org.apache.maven.index.packer.IndexPackingRequest;
import org.apache.maven.index.updater.IndexUpdateRequest;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Collectors;
}
}
+ @Override
+ public ArchivaIndexingContext mergeContexts(Repository destinationRepo, List<ArchivaIndexingContext> contexts,
+ boolean packIndex) throws UnsupportedOperationException,
+ IndexCreationFailedException, IllegalArgumentException {
+ if (!destinationRepo.supportsFeature(IndexCreationFeature.class)) {
+ throw new IllegalArgumentException("The given repository does not support the indexcreation feature");
+ }
+ Path mergedIndexDirectory = null;
+ try {
+ mergedIndexDirectory = Files.createTempDirectory("archivaMergedIndex");
+ } catch (IOException e) {
+ log.error("Could not create temporary directory for merged index: {}", e.getMessage(), e);
+ throw new IndexCreationFailedException("IO error while creating temporary directory for merged index: "+e.getMessage(), e);
+ }
+ IndexCreationFeature indexCreationFeature = destinationRepo.getFeature(IndexCreationFeature.class).get();
+ if (indexCreationFeature.getLocalIndexPath()== null) {
+ throw new IllegalArgumentException("The given repository does not have a local index path");
+ }
+ StorageAsset destinationPath = indexCreationFeature.getLocalIndexPath();
+
+ String tempRepoId = mergedIndexDirectory.getFileName().toString();
+
+ try
+ {
+ Path indexLocation = destinationPath.getFilePath();
+
+ List<IndexingContext> members = contexts.stream( ).filter(ctx -> ctx.supports(IndexingContext.class)).map( ctx ->
+ {
+ try {
+ return ctx.getBaseContext(IndexingContext.class);
+ } catch (UnsupportedBaseContextException e) {
+ // does not happen here
+ return null;
+ }
+ }).filter( Objects::nonNull ).collect( Collectors.toList() );
+ ContextMemberProvider memberProvider = new StaticContextMemberProvider(members);
+ IndexingContext mergedCtx = indexer.createMergedIndexingContext( tempRepoId, tempRepoId, mergedIndexDirectory.toFile(),
+ indexLocation.toFile(), true, memberProvider);
+ mergedCtx.optimize();
+
+ if ( packIndex )
+ {
+ IndexPackingRequest request = new IndexPackingRequest( mergedCtx, //
+ mergedCtx.acquireIndexSearcher().getIndexReader(), //
+ indexLocation.toFile() );
+ indexPacker.packIndex( request );
+ }
+
+ return new MavenIndexContext(destinationRepo, mergedCtx);
+ }
+ catch ( IOException e)
+ {
+ throw new IndexCreationFailedException( "IO Error during index merge: "+ e.getMessage(), e );
+ }
+ }
+
private StorageAsset getIndexPath(URI indexDir, Path repoDir, String defaultDir) throws IOException
{
String indexPath = indexDir.getPath();
import java.net.URI;
import java.util.Collection;
+import java.util.List;
/**
* @author Martin Stockhammer <martin_s@apache.org>
public void updateLocalIndexPath(Repository repo) {
}
+
+ @Override
+ public ArchivaIndexingContext mergeContexts(Repository destinationRepo, List<ArchivaIndexingContext> contexts, boolean packIndex) throws UnsupportedOperationException, IndexCreationFailedException {
+ return null;
+ }
}
repositoryGroup.setSchedulingDefinition(configuration.getCronExpression());
if (repositoryGroup.supportsFeature( IndexCreationFeature.class )) {
IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
- try
+ indexCreationFeature.setIndexPath( getURIFromString(configuration.getMergedIndexPath()) );
+ Path localPath = Paths.get(configuration.getMergedIndexPath());
+ if (localPath.isAbsolute()) {
+ indexCreationFeature.setLocalIndexPath( new FilesystemAsset(localPath.getFileName().toString(), localPath) );
+ } else
{
- indexCreationFeature.setIndexPath( new URI(configuration.getMergedIndexPath()) );
- Path localPath = Paths.get(indexCreationFeature.getIndexPath());
- if (localPath.isAbsolute()) {
- indexCreationFeature.setLocalIndexPath( new FilesystemAsset(localPath.getFileName().toString(), localPath) );
- } else
- {
- indexCreationFeature.setLocalIndexPath( new FilesystemAsset(localPath.toString(), archivaConfiguration.getRepositoryGroupBaseDir( ).resolve( localPath )));
- }
- }
- catch ( URISyntaxException e )
- {
- log.error("Could not set the index path for repository group {}", repositoryGroup.getId());
+ indexCreationFeature.setLocalIndexPath( new FilesystemAsset(localPath.toString(), archivaConfiguration.getRepositoryGroupBaseDir( ).resolve( localPath )));
}
}
// References to other repositories are set filled by the registry
}
}
+ @Override
+ public ArchivaIndexingContext mergeContexts(Repository destinationRepo, List<ArchivaIndexingContext> contexts, boolean packIndex) throws UnsupportedOperationException, IndexCreationFailedException {
+ return null;
+ }
+
private StorageAsset getIndexPath( Repository repo) throws IOException {
IndexCreationFeature icf = repo.getFeature(IndexCreationFeature.class).get();
assertEquals("Group 2", grp.getName());
assertEquals("0 0 03 ? * MON", grp.getSchedulingDefinition());
IndexCreationFeature indexCreationFeature = grp.getFeature( IndexCreationFeature.class ).get();
- assertEquals(".index-abc", indexCreationFeature.getIndexPath());
+ try {
+ assertEquals(new URI("file://.index-abc"), indexCreationFeature.getIndexPath());
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
assertEquals(504, grp.getMergedIndexTTL());
assertEquals(0, grp.getRepositories().size());
// assertTrue(grp.getRepositories().stream().anyMatch(r -> "test01".equals(r.getId())));