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.scheduler.MergedRemoteIndexesScheduler; 032import org.apache.commons.lang.StringUtils; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035import org.springframework.stereotype.Service; 036 037import javax.annotation.PostConstruct; 038import javax.inject.Inject; 039import java.io.File; 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collections; 043import java.util.HashMap; 044import java.util.List; 045import java.util.Map; 046import java.util.regex.Matcher; 047import java.util.regex.Pattern; 048 049/** 050 * @author Olivier Lamy 051 */ 052@Service("repositoryGroupAdmin#default") 053public class DefaultRepositoryGroupAdmin 054 extends AbstractRepositoryAdmin 055 implements RepositoryGroupAdmin 056{ 057 058 private Logger log = LoggerFactory.getLogger( getClass() ); 059 060 private static final Pattern REPO_GROUP_ID_PATTERN = Pattern.compile( "[A-Za-z0-9\\._\\-]+" ); 061 062 @Inject 063 private ManagedRepositoryAdmin managedRepositoryAdmin; 064 065 @Inject 066 private MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler; 067 068 private File groupsDirectory; 069 070 @PostConstruct 071 public void initialize() 072 { 073 String appServerBase = getRegistry().getString( "appserver.base" ); 074 groupsDirectory = new File( appServerBase + File.separatorChar + "groups" ); 075 if ( !groupsDirectory.exists() ) 076 { 077 groupsDirectory.mkdirs(); 078 } 079 080 try 081 { 082 for ( RepositoryGroup repositoryGroup : getRepositoriesGroups() ) 083 { 084 mergedRemoteIndexesScheduler.schedule( repositoryGroup, 085 getMergedIndexDirectory( repositoryGroup.getId() ) ); 086 // create the directory for each group if not exists 087 File groupPath = new File( groupsDirectory, repositoryGroup.getId() ); 088 if ( !groupPath.exists() ) 089 { 090 groupPath.mkdirs(); 091 } 092 } 093 } 094 catch ( RepositoryAdminException e ) 095 { 096 log.warn( "fail to getRepositoriesGroups {}", e.getMessage(), e ); 097 } 098 099 } 100 101 102 @Override 103 public File getMergedIndexDirectory( String repositoryGroupId ) 104 { 105 return new File( groupsDirectory, repositoryGroupId ); 106 } 107 108 @Override 109 public List<RepositoryGroup> getRepositoriesGroups() 110 throws RepositoryAdminException 111 { 112 List<RepositoryGroup> repositoriesGroups = 113 new ArrayList<>( getArchivaConfiguration().getConfiguration().getRepositoryGroups().size() ); 114 115 for ( RepositoryGroupConfiguration repositoryGroupConfiguration : getArchivaConfiguration().getConfiguration().getRepositoryGroups() ) 116 { 117 repositoriesGroups.add( new RepositoryGroup( repositoryGroupConfiguration.getId(), new ArrayList<String>( 118 repositoryGroupConfiguration.getRepositories() ) ).mergedIndexPath( 119 repositoryGroupConfiguration.getMergedIndexPath() ).mergedIndexTtl( 120 repositoryGroupConfiguration.getMergedIndexTtl() ).cronExpression( 121 repositoryGroupConfiguration.getCronExpression() ) ); 122 } 123 124 return repositoriesGroups; 125 } 126 127 @Override 128 public RepositoryGroup getRepositoryGroup( String repositoryGroupId ) 129 throws RepositoryAdminException 130 { 131 List<RepositoryGroup> repositoriesGroups = getRepositoriesGroups(); 132 for ( RepositoryGroup repositoryGroup : repositoriesGroups ) 133 { 134 if ( StringUtils.equals( repositoryGroupId, repositoryGroup.getId() ) ) 135 { 136 return repositoryGroup; 137 } 138 } 139 return null; 140 } 141 142 @Override 143 public Boolean addRepositoryGroup( RepositoryGroup repositoryGroup, AuditInformation auditInformation ) 144 throws RepositoryAdminException 145 { 146 validateRepositoryGroup( repositoryGroup, false ); 147 validateManagedRepositoriesExists( repositoryGroup.getRepositories() ); 148 149 RepositoryGroupConfiguration repositoryGroupConfiguration = new RepositoryGroupConfiguration(); 150 repositoryGroupConfiguration.setId( repositoryGroup.getId() ); 151 repositoryGroupConfiguration.setRepositories( repositoryGroup.getRepositories() ); 152 repositoryGroupConfiguration.setMergedIndexPath( repositoryGroup.getMergedIndexPath() ); 153 repositoryGroupConfiguration.setMergedIndexTtl( repositoryGroup.getMergedIndexTtl() ); 154 repositoryGroupConfiguration.setCronExpression( repositoryGroup.getCronExpression() ); 155 Configuration configuration = getArchivaConfiguration().getConfiguration(); 156 configuration.addRepositoryGroup( repositoryGroupConfiguration ); 157 saveConfiguration( configuration ); 158 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.ADD_REPO_GROUP, auditInformation ); 159 mergedRemoteIndexesScheduler.schedule( repositoryGroup, getMergedIndexDirectory( repositoryGroup.getId() ) ); 160 return Boolean.TRUE; 161 } 162 163 @Override 164 public Boolean deleteRepositoryGroup( String repositoryGroupId, AuditInformation auditInformation ) 165 throws RepositoryAdminException 166 { 167 Configuration configuration = getArchivaConfiguration().getConfiguration(); 168 RepositoryGroupConfiguration repositoryGroupConfiguration = 169 configuration.getRepositoryGroupsAsMap().get( repositoryGroupId ); 170 mergedRemoteIndexesScheduler.unschedule( 171 new RepositoryGroup( repositoryGroupId, Collections.<String>emptyList() ) ); 172 if ( repositoryGroupConfiguration == null ) 173 { 174 throw new RepositoryAdminException( 175 "repositoryGroup with id " + repositoryGroupId + " doesn't not exists so cannot remove" ); 176 } 177 configuration.removeRepositoryGroup( repositoryGroupConfiguration ); 178 triggerAuditEvent( repositoryGroupId, null, AuditEvent.DELETE_REPO_GROUP, auditInformation ); 179 180 return Boolean.TRUE; 181 } 182 183 @Override 184 public Boolean updateRepositoryGroup( RepositoryGroup repositoryGroup, AuditInformation auditInformation ) 185 throws RepositoryAdminException 186 { 187 return updateRepositoryGroup( repositoryGroup, auditInformation, true ); 188 } 189 190 private Boolean updateRepositoryGroup( RepositoryGroup repositoryGroup, AuditInformation auditInformation, 191 boolean triggerAuditEvent ) 192 throws RepositoryAdminException 193 { 194 validateRepositoryGroup( repositoryGroup, true ); 195 validateManagedRepositoriesExists( repositoryGroup.getRepositories() ); 196 Configuration configuration = getArchivaConfiguration().getConfiguration(); 197 198 RepositoryGroupConfiguration repositoryGroupConfiguration = 199 configuration.getRepositoryGroupsAsMap().get( repositoryGroup.getId() ); 200 201 configuration.removeRepositoryGroup( repositoryGroupConfiguration ); 202 203 repositoryGroupConfiguration.setRepositories( repositoryGroup.getRepositories() ); 204 repositoryGroupConfiguration.setMergedIndexPath( repositoryGroup.getMergedIndexPath() ); 205 repositoryGroupConfiguration.setMergedIndexTtl( repositoryGroup.getMergedIndexTtl() ); 206 repositoryGroupConfiguration.setCronExpression( repositoryGroup.getCronExpression() ); 207 configuration.addRepositoryGroup( repositoryGroupConfiguration ); 208 209 saveConfiguration( configuration ); 210 if ( triggerAuditEvent ) 211 { 212 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.MODIFY_REPO_GROUP, auditInformation ); 213 } 214 mergedRemoteIndexesScheduler.unschedule( repositoryGroup ); 215 mergedRemoteIndexesScheduler.schedule( repositoryGroup, getMergedIndexDirectory( repositoryGroup.getId() ) ); 216 return Boolean.TRUE; 217 } 218 219 220 @Override 221 public Boolean addRepositoryToGroup( String repositoryGroupId, String repositoryId, 222 AuditInformation auditInformation ) 223 throws RepositoryAdminException 224 { 225 RepositoryGroup repositoryGroup = getRepositoryGroup( repositoryGroupId ); 226 if ( repositoryGroup == null ) 227 { 228 throw new RepositoryAdminException( 229 "repositoryGroup with id " + repositoryGroupId + " doesn't not exists so cannot add repository to it" ); 230 } 231 232 if ( repositoryGroup.getRepositories().contains( repositoryId ) ) 233 { 234 throw new RepositoryAdminException( 235 "repositoryGroup with id " + repositoryGroupId + " already contain repository with id" + repositoryId ); 236 } 237 validateManagedRepositoriesExists( Arrays.asList( repositoryId ) ); 238 239 repositoryGroup.addRepository( repositoryId ); 240 updateRepositoryGroup( repositoryGroup, auditInformation, false ); 241 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.ADD_REPO_TO_GROUP, auditInformation ); 242 return Boolean.TRUE; 243 } 244 245 @Override 246 public Boolean deleteRepositoryFromGroup( String repositoryGroupId, String repositoryId, 247 AuditInformation auditInformation ) 248 throws RepositoryAdminException 249 { 250 RepositoryGroup repositoryGroup = getRepositoryGroup( repositoryGroupId ); 251 if ( repositoryGroup == null ) 252 { 253 throw new RepositoryAdminException( "repositoryGroup with id " + repositoryGroupId 254 + " doesn't not exists so cannot remove repository from it" ); 255 } 256 257 if ( !repositoryGroup.getRepositories().contains( repositoryId ) ) 258 { 259 throw new RepositoryAdminException( 260 "repositoryGroup with id " + repositoryGroupId + " doesn't not contains repository with id" 261 + repositoryId 262 ); 263 } 264 265 repositoryGroup.removeRepository( repositoryId ); 266 updateRepositoryGroup( repositoryGroup, auditInformation, false ); 267 triggerAuditEvent( repositoryGroup.getId(), null, AuditEvent.DELETE_REPO_FROM_GROUP, auditInformation ); 268 return Boolean.TRUE; 269 } 270 271 @Override 272 public Map<String, RepositoryGroup> getRepositoryGroupsAsMap() 273 throws RepositoryAdminException 274 { 275 List<RepositoryGroup> repositoriesGroups = getRepositoriesGroups(); 276 Map<String, RepositoryGroup> map = new HashMap<>( repositoriesGroups.size() ); 277 for ( RepositoryGroup repositoryGroup : repositoriesGroups ) 278 { 279 map.put( repositoryGroup.getId(), repositoryGroup ); 280 } 281 return map; 282 } 283 284 @Override 285 public Map<String, List<String>> getGroupToRepositoryMap() 286 throws RepositoryAdminException 287 { 288 289 Map<String, List<String>> map = new HashMap<>(); 290 291 for ( ManagedRepository repo : getManagedRepositoryAdmin().getManagedRepositories() ) 292 { 293 for ( RepositoryGroup group : getRepositoriesGroups() ) 294 { 295 if ( !group.getRepositories().contains( repo.getId() ) ) 296 { 297 String groupId = group.getId(); 298 List<String> repos = map.get( groupId ); 299 if ( repos == null ) 300 { 301 repos = new ArrayList<>(); 302 map.put( groupId, repos ); 303 } 304 repos.add( repo.getId() ); 305 } 306 } 307 } 308 return map; 309 } 310 311 @Override 312 public Map<String, List<String>> getRepositoryToGroupMap() 313 throws RepositoryAdminException 314 { 315 Map<String, List<String>> map = new HashMap<>(); 316 317 for ( RepositoryGroup group : getRepositoriesGroups() ) 318 { 319 for ( String repositoryId : group.getRepositories() ) 320 { 321 List<String> groups = map.get( repositoryId ); 322 if ( groups == null ) 323 { 324 groups = new ArrayList<>(); 325 map.put( repositoryId, groups ); 326 } 327 groups.add( group.getId() ); 328 } 329 } 330 return map; 331 } 332 333 public Boolean validateRepositoryGroup( RepositoryGroup repositoryGroup, boolean updateMode ) 334 throws RepositoryAdminException 335 { 336 String repoGroupId = repositoryGroup.getId(); 337 if ( StringUtils.isBlank( repoGroupId ) ) 338 { 339 throw new RepositoryAdminException( "repositoryGroup id cannot be empty" ); 340 } 341 342 if ( repoGroupId.length() > 100 ) 343 { 344 throw new RepositoryAdminException( 345 "Identifier [" + repoGroupId + "] is over the maximum limit of 100 characters" ); 346 347 } 348 349 Matcher matcher = REPO_GROUP_ID_PATTERN.matcher( repoGroupId ); 350 if ( !matcher.matches() ) 351 { 352 throw new RepositoryAdminException( 353 "Invalid character(s) found in identifier. Only the following characters are allowed: alphanumeric, '.', '-' and '_'" ); 354 } 355 356 if ( repositoryGroup.getMergedIndexTtl() <= 0 ) 357 { 358 throw new RepositoryAdminException( "Merged Index TTL must be greater than 0." ); 359 } 360 361 Configuration configuration = getArchivaConfiguration().getConfiguration(); 362 363 if ( configuration.getRepositoryGroupsAsMap().containsKey( repoGroupId ) ) 364 { 365 if ( !updateMode ) 366 { 367 throw new RepositoryAdminException( "Unable to add new repository group with id [" + repoGroupId 368 + "], that id already exists as a repository group." ); 369 } 370 } 371 else if ( configuration.getManagedRepositoriesAsMap().containsKey( repoGroupId ) ) 372 { 373 throw new RepositoryAdminException( "Unable to add new repository group with id [" + repoGroupId 374 + "], that id already exists as a managed repository." ); 375 } 376 else if ( configuration.getRemoteRepositoriesAsMap().containsKey( repoGroupId ) ) 377 { 378 throw new RepositoryAdminException( "Unable to add new repository group with id [" + repoGroupId 379 + "], that id already exists as a remote repository." ); 380 } 381 382 return Boolean.TRUE; 383 } 384 385 private void validateManagedRepositoriesExists( List<String> managedRepositoriesIds ) 386 throws RepositoryAdminException 387 { 388 for ( String id : managedRepositoriesIds ) 389 { 390 if ( getManagedRepositoryAdmin().getManagedRepository( id ) == null ) 391 { 392 throw new RepositoryAdminException( 393 "managedRepository with id " + id + " not exists so cannot be used in a repositoryGroup" ); 394 } 395 } 396 } 397 398 public ManagedRepositoryAdmin getManagedRepositoryAdmin() 399 { 400 return managedRepositoryAdmin; 401 } 402 403 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin ) 404 { 405 this.managedRepositoryAdmin = managedRepositoryAdmin; 406 } 407}