001package org.apache.archiva.admin.repository.group; 002/* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020 021import org.apache.archiva.admin.model.AuditInformation; 022import org.apache.archiva.admin.model.RepositoryAdminException; 023import org.apache.archiva.admin.model.beans.ManagedRepository; 024import org.apache.archiva.admin.model.beans.RepositoryGroup; 025import org.apache.archiva.admin.model.group.RepositoryGroupAdmin; 026import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; 027import org.apache.archiva.admin.repository.AbstractRepositoryAdmin; 028import org.apache.archiva.configuration.Configuration; 029import org.apache.archiva.configuration.RepositoryGroupConfiguration; 030import org.apache.archiva.metadata.model.facets.AuditEvent; 031import org.apache.archiva.indexer.merger.MergedRemoteIndexesScheduler; 032import org.apache.archiva.repository.EditableRepositoryGroup; 033import org.apache.archiva.repository.RepositoryException; 034import org.apache.archiva.repository.RepositoryRegistry; 035import org.apache.archiva.repository.features.IndexCreationFeature; 036import org.apache.archiva.repository.storage.StorageAsset; 037import org.apache.commons.lang3.StringUtils; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040import org.springframework.stereotype.Service; 041 042import javax.annotation.PostConstruct; 043import javax.inject.Inject; 044import javax.inject.Named; 045import java.io.IOException; 046import java.nio.file.Files; 047import java.nio.file.Path; 048import java.nio.file.Paths; 049import java.util.ArrayList; 050import java.util.HashMap; 051import java.util.List; 052import java.util.Map; 053import java.util.regex.Matcher; 054import java.util.regex.Pattern; 055import java.util.stream.Collectors; 056 057/** 058 * @author Olivier Lamy 059 */ 060@Service("repositoryGroupAdmin#default") 061public class DefaultRepositoryGroupAdmin 062 extends AbstractRepositoryAdmin 063 implements RepositoryGroupAdmin 064{ 065 066 private Logger log = LoggerFactory.getLogger( getClass() ); 067 068 private static final Pattern REPO_GROUP_ID_PATTERN = Pattern.compile( "[A-Za-z0-9\\._\\-]+" ); 069 070 @Inject 071 private ManagedRepositoryAdmin managedRepositoryAdmin; 072 073 @Inject 074 @Named("mergedRemoteIndexesScheduler#default") 075 private MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler; 076 077 @Inject 078 private RepositoryRegistry repositoryRegistry; 079 080 private Path groupsDirectory; 081 082 @PostConstruct 083 public void initialize() 084 { 085 String appServerBase = getRegistry().getString( "appserver.base" ); 086 groupsDirectory = Paths.get( appServerBase, "groups" ); 087 if ( !Files.exists(groupsDirectory) ) 088 { 089 Files.exists(groupsDirectory); 090 } 091 092 for ( org.apache.archiva.repository.RepositoryGroup repositoryGroup : repositoryRegistry.getRepositoryGroups() ) 093 { 094 mergedRemoteIndexesScheduler.schedule( repositoryGroup, 095 getMergedIndexDirectory( repositoryGroup.getId() )); 096 // create the directory for each group if not exists 097 Path groupPath = groupsDirectory.resolve(repositoryGroup.getId() ); 098 if ( !Files.exists(groupPath) ) 099 { 100 try { 101 Files.createDirectories(groupPath); 102 } catch (IOException e) { 103 log.error("Could not create directory {}", groupPath); 104 } 105 } 106 } 107 108 } 109 110 111 @Override 112 public StorageAsset getMergedIndexDirectory(String repositoryGroupId ) 113 { 114 org.apache.archiva.repository.RepositoryGroup group = repositoryRegistry.getRepositoryGroup(repositoryGroupId); 115 if (group!=null) { 116 return group.getFeature(IndexCreationFeature.class).get().getLocalIndexPath(); 117 } else { 118 return null; 119 } 120 } 121 122 @Override 123 public List<RepositoryGroup> getRepositoriesGroups() { 124 return repositoryRegistry.getRepositoryGroups().stream().map( r -> convertRepositoryGroupObject( r ) ).collect( Collectors.toList()); 125 } 126 127 @Override 128 public RepositoryGroup getRepositoryGroup( String repositoryGroupId ) { 129 return convertRepositoryGroupObject( repositoryRegistry.getRepositoryGroup( repositoryGroupId ) ); 130 } 131 132 @Override 133 public Boolean addRepositoryGroup( RepositoryGroup repositoryGroup, AuditInformation auditInformation ) 134 throws RepositoryAdminException 135 { 136 validateRepositoryGroup( repositoryGroup, false ); 137 validateManagedRepositoriesExists( repositoryGroup.getRepositories() ); 138 139 RepositoryGroupConfiguration repositoryGroupConfiguration = new RepositoryGroupConfiguration(); 140 repositoryGroupConfiguration.setId( repositoryGroup.getId() ); 141 repositoryGroupConfiguration.setRepositories( repositoryGroup.getRepositories() ); 142 repositoryGroupConfiguration.setMergedIndexPath( repositoryGroup.getMergedIndexPath() ); 143 repositoryGroupConfiguration.setMergedIndexTtl( repositoryGroup.getMergedIndexTtl() ); 144 repositoryGroupConfiguration.setCronExpression( StringUtils.isEmpty(repositoryGroup.getCronExpression()) ? "0 0 03 ? * MON" : repositoryGroup.getCronExpression() ); 145 146 try { 147 repositoryRegistry.putRepositoryGroup(repositoryGroupConfiguration); 148 } catch (RepositoryException e) { 149 e.printStackTrace(); 150 } 151 152 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.ADD_REPO_GROUP, auditInformation ); 153 mergedRemoteIndexesScheduler.schedule( repositoryRegistry.getRepositoryGroup( repositoryGroup.getId()), getMergedIndexDirectory( repositoryGroup.getId() ) ); 154 return Boolean.TRUE; 155 } 156 157 @Override 158 public Boolean deleteRepositoryGroup( String repositoryGroupId, AuditInformation auditInformation ) 159 throws RepositoryAdminException 160 { 161 162 org.apache.archiva.repository.RepositoryGroup repositoryGroup = repositoryRegistry.getRepositoryGroup(repositoryGroupId); 163 try { 164 repositoryRegistry.removeRepositoryGroup(repositoryGroup); 165 } catch (RepositoryException e) { 166 log.error("Removal of repository group {} failed: {}", repositoryGroup.getId(), e.getMessage(), e); 167 throw new RepositoryAdminException("Removal of repository failed: " + e.getMessage(), e); 168 } 169 mergedRemoteIndexesScheduler.unschedule( 170 repositoryGroup ); 171 triggerAuditEvent( repositoryGroupId, null, AuditEvent.DELETE_REPO_GROUP, auditInformation ); 172 173 return Boolean.TRUE; 174 } 175 176 @Override 177 public Boolean updateRepositoryGroup( RepositoryGroup repositoryGroup, AuditInformation auditInformation ) 178 throws RepositoryAdminException 179 { 180 return updateRepositoryGroup( repositoryGroup, auditInformation, true ); 181 } 182 183 private Boolean updateRepositoryGroup( RepositoryGroup repositoryGroup, AuditInformation auditInformation, 184 boolean triggerAuditEvent ) 185 throws RepositoryAdminException 186 { 187 validateRepositoryGroup( repositoryGroup, true ); 188 validateManagedRepositoriesExists( repositoryGroup.getRepositories() ); 189 190 191 Configuration configuration = getArchivaConfiguration().getConfiguration(); 192 193 RepositoryGroupConfiguration repositoryGroupConfiguration = 194 configuration.getRepositoryGroupsAsMap().get( repositoryGroup.getId() ); 195 196 repositoryGroupConfiguration.setRepositories( repositoryGroup.getRepositories() ); 197 repositoryGroupConfiguration.setMergedIndexPath( repositoryGroup.getMergedIndexPath() ); 198 repositoryGroupConfiguration.setMergedIndexTtl( repositoryGroup.getMergedIndexTtl() ); 199 repositoryGroupConfiguration.setCronExpression( repositoryGroup.getCronExpression() ); 200 try { 201 repositoryRegistry.putRepositoryGroup(repositoryGroupConfiguration); 202 } catch (RepositoryException e) { 203 e.printStackTrace(); 204 } 205 206 org.apache.archiva.repository.RepositoryGroup rg = repositoryRegistry.getRepositoryGroup( repositoryGroup.getId( ) ); 207 mergedRemoteIndexesScheduler.unschedule( rg ); 208 mergedRemoteIndexesScheduler.schedule( rg, getMergedIndexDirectory( repositoryGroup.getId() ) ); 209 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.MODIFY_REPO_GROUP, auditInformation ); 210 return Boolean.TRUE; 211 } 212 213 214 @Override 215 public Boolean addRepositoryToGroup( String repositoryGroupId, String repositoryId, 216 AuditInformation auditInformation ) 217 throws RepositoryAdminException 218 { 219 org.apache.archiva.repository.RepositoryGroup repositoryGroup = repositoryRegistry.getRepositoryGroup( repositoryGroupId ); 220 if ( repositoryGroup == null ) 221 { 222 throw new RepositoryAdminException( 223 "repositoryGroup with id " + repositoryGroupId + " doesn't not exists so cannot add repository to it" ); 224 } 225 226 if (!(repositoryGroup instanceof EditableRepositoryGroup)) { 227 throw new RepositoryAdminException("The repository group is not editable "+repositoryGroupId); 228 } 229 EditableRepositoryGroup editableRepositoryGroup = (EditableRepositoryGroup) repositoryGroup; 230 if ( editableRepositoryGroup.getRepositories().stream().anyMatch( repo -> repositoryId.equals(repo.getId())) ) 231 { 232 throw new RepositoryAdminException( 233 "repositoryGroup with id " + repositoryGroupId + " already contain repository with id" + repositoryId ); 234 } 235 org.apache.archiva.repository.ManagedRepository managedRepo = repositoryRegistry.getManagedRepository(repositoryId); 236 if (managedRepo==null) { 237 throw new RepositoryAdminException("Repository with id "+repositoryId+" does not exist" ); 238 } 239 240 editableRepositoryGroup.addRepository( managedRepo ); 241 try { 242 repositoryRegistry.putRepositoryGroup(editableRepositoryGroup); 243 } catch (RepositoryException e) { 244 throw new RepositoryAdminException("Could not store the repository group "+repositoryGroupId, e); 245 } 246 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.ADD_REPO_TO_GROUP, auditInformation ); 247 return Boolean.TRUE; 248 } 249 250 @Override 251 public Boolean deleteRepositoryFromGroup( String repositoryGroupId, String repositoryId, 252 AuditInformation auditInformation ) 253 throws RepositoryAdminException 254 { 255 org.apache.archiva.repository.RepositoryGroup repositoryGroup = repositoryRegistry.getRepositoryGroup( repositoryGroupId ); 256 if ( repositoryGroup == null ) 257 { 258 throw new RepositoryAdminException( "repositoryGroup with id " + repositoryGroupId 259 + " doesn't not exists so cannot remove repository from it" ); 260 } 261 262 if ( !repositoryGroup.getRepositories().stream().anyMatch( repo -> repositoryId.equals(repo.getId()) ) ) 263 { 264 throw new RepositoryAdminException( 265 "repositoryGroup with id " + repositoryGroupId + " doesn't not contains repository with id" 266 + repositoryId 267 ); 268 } 269 if (!(repositoryGroup instanceof EditableRepositoryGroup)) { 270 throw new RepositoryAdminException("Repository group is not editable " + repositoryGroupId); 271 } 272 EditableRepositoryGroup editableRepositoryGroup = (EditableRepositoryGroup) repositoryGroup; 273 274 editableRepositoryGroup.removeRepository( repositoryId ); 275 try { 276 repositoryRegistry.putRepositoryGroup(editableRepositoryGroup); 277 } catch (RepositoryException e) { 278 throw new RepositoryAdminException("Could not store repository group " + repositoryGroupId, e); 279 } 280 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.DELETE_REPO_FROM_GROUP, auditInformation ); 281 return Boolean.TRUE; 282 } 283 284 @Override 285 public Map<String, RepositoryGroup> getRepositoryGroupsAsMap() 286 throws RepositoryAdminException 287 { 288 List<RepositoryGroup> repositoriesGroups = getRepositoriesGroups(); 289 Map<String, RepositoryGroup> map = new HashMap<>( repositoriesGroups.size() ); 290 for ( RepositoryGroup repositoryGroup : repositoriesGroups ) 291 { 292 map.put( repositoryGroup.getId(), repositoryGroup ); 293 } 294 return map; 295 } 296 297 @Override 298 public Map<String, List<String>> getGroupToRepositoryMap() 299 throws RepositoryAdminException 300 { 301 302 Map<String, List<String>> map = new HashMap<>(); 303 304 for ( ManagedRepository repo : getManagedRepositoryAdmin().getManagedRepositories() ) 305 { 306 for ( RepositoryGroup group : getRepositoriesGroups() ) 307 { 308 if ( !group.getRepositories().contains( repo.getId() ) ) 309 { 310 String groupId = group.getId(); 311 List<String> repos = map.get( groupId ); 312 if ( repos == null ) 313 { 314 repos = new ArrayList<>(); 315 map.put( groupId, repos ); 316 } 317 repos.add( repo.getId() ); 318 } 319 } 320 } 321 return map; 322 } 323 324 @Override 325 public Map<String, List<String>> getRepositoryToGroupMap() 326 throws RepositoryAdminException 327 { 328 Map<String, List<String>> map = new HashMap<>(); 329 330 for ( RepositoryGroup group : getRepositoriesGroups() ) 331 { 332 for ( String repositoryId : group.getRepositories() ) 333 { 334 List<String> groups = map.get( repositoryId ); 335 if ( groups == null ) 336 { 337 groups = new ArrayList<>(); 338 map.put( repositoryId, groups ); 339 } 340 groups.add( group.getId() ); 341 } 342 } 343 return map; 344 } 345 346 public Boolean validateRepositoryGroup( RepositoryGroup repositoryGroup, boolean updateMode ) 347 throws RepositoryAdminException 348 { 349 String repoGroupId = repositoryGroup.getId(); 350 if ( StringUtils.isBlank( repoGroupId ) ) 351 { 352 throw new RepositoryAdminException( "repositoryGroup id cannot be empty" ); 353 } 354 355 if ( repoGroupId.length() > 100 ) 356 { 357 throw new RepositoryAdminException( 358 "Identifier [" + repoGroupId + "] is over the maximum limit of 100 characters" ); 359 360 } 361 362 Matcher matcher = REPO_GROUP_ID_PATTERN.matcher( repoGroupId ); 363 if ( !matcher.matches() ) 364 { 365 throw new RepositoryAdminException( 366 "Invalid character(s) found in identifier. Only the following characters are allowed: alphanumeric, '.', '-' and '_'" ); 367 } 368 369 if ( repositoryGroup.getMergedIndexTtl() <= 0 ) 370 { 371 throw new RepositoryAdminException( "Merged Index TTL must be greater than 0." ); 372 } 373 374 Configuration configuration = getArchivaConfiguration().getConfiguration(); 375 376 if ( configuration.getRepositoryGroupsAsMap().containsKey( repoGroupId ) ) 377 { 378 if ( !updateMode ) 379 { 380 throw new RepositoryAdminException( "Unable to add new repository group with id [" + repoGroupId 381 + "], that id already exists as a repository group." ); 382 } 383 } 384 else if ( configuration.getManagedRepositoriesAsMap().containsKey( repoGroupId ) ) 385 { 386 throw new RepositoryAdminException( "Unable to add new repository group with id [" + repoGroupId 387 + "], that id already exists as a managed repository." ); 388 } 389 else if ( configuration.getRemoteRepositoriesAsMap().containsKey( repoGroupId ) ) 390 { 391 throw new RepositoryAdminException( "Unable to add new repository group with id [" + repoGroupId 392 + "], that id already exists as a remote repository." ); 393 } 394 395 return Boolean.TRUE; 396 } 397 398 private void validateManagedRepositoriesExists( List<String> managedRepositoriesIds ) 399 throws RepositoryAdminException 400 { 401 for ( String id : managedRepositoriesIds ) 402 { 403 if ( getManagedRepositoryAdmin().getManagedRepository( id ) == null ) 404 { 405 throw new RepositoryAdminException( 406 "managedRepository with id " + id + " not exists so cannot be used in a repositoryGroup" ); 407 } 408 } 409 } 410 411 public ManagedRepositoryAdmin getManagedRepositoryAdmin() 412 { 413 return managedRepositoryAdmin; 414 } 415 416 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin ) 417 { 418 this.managedRepositoryAdmin = managedRepositoryAdmin; 419 } 420 421 private RepositoryGroup convertRepositoryGroupObject( org.apache.archiva.repository.RepositoryGroup group ) { 422 RepositoryGroup rg = new RepositoryGroup( group.getId( ), group.getRepositories().stream().map(r -> r.getId()).collect( Collectors.toList()) ); 423 if (group.supportsFeature( IndexCreationFeature.class )) 424 { 425 IndexCreationFeature indexCreationFeature = group.getFeature( IndexCreationFeature.class ).get(); 426 rg.setMergedIndexPath( indexCreationFeature.getIndexPath().getPath() ); 427 } 428 rg.setCronExpression( group.getSchedulingDefinition() ); 429 rg.setMergedIndexTtl( group.getMergedIndexTTL() ); 430 return rg; 431 } 432}