]> source.dussan.org Git - archiva.git/blob
bee98694895d89f864b10a088698f503bfbfca4e
[archiva.git] /
1 package org.apache.archiva.admin.repository.managed;
2 /*
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  */
20
21 import org.apache.archiva.admin.model.AuditInformation;
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.managed.ManagedRepository;
24 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
25 import org.apache.archiva.admin.repository.AbstractRepositoryAdmin;
26 import org.apache.archiva.audit.AuditEvent;
27 import org.apache.archiva.metadata.repository.MetadataRepository;
28 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
29 import org.apache.archiva.metadata.repository.RepositorySession;
30 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
31 import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
32 import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler;
33 import org.apache.archiva.scheduler.repository.RepositoryTask;
34 import org.apache.archiva.security.common.ArchivaRoleConstants;
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.commons.validator.GenericValidator;
38 import org.apache.maven.archiva.configuration.Configuration;
39 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
40 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
41 import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration;
42 import org.codehaus.plexus.redback.role.RoleManager;
43 import org.codehaus.plexus.redback.role.RoleManagerException;
44 import org.codehaus.plexus.taskqueue.TaskQueueException;
45 import org.codehaus.redback.components.scheduler.CronExpressionValidator;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.stereotype.Service;
49
50 import javax.inject.Inject;
51 import javax.inject.Named;
52 import java.io.File;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59
60 /**
61  * FIXME remove all generic Exception to have usefull ones
62  * FIXME review the staging mechanism to have a per user session one
63  *
64  * @author Olivier Lamy
65  */
66 @Service( "managedRepositoryAdmin#default" )
67 public class DefaultManagedRepositoryAdmin
68     extends AbstractRepositoryAdmin
69     implements ManagedRepositoryAdmin
70 {
71
72     public static final String REPOSITORY_LOCATION_VALID_EXPRESSION = "^[-a-zA-Z0-9._/~:?!&=\\\\]+$";
73
74     private Logger log = LoggerFactory.getLogger( getClass() );
75
76     public static final String STAGE_REPO_ID_END = "-stage";
77
78
79     @Inject
80     @Named( value = "archivaTaskScheduler#repository" )
81     private RepositoryArchivaTaskScheduler repositoryTaskScheduler;
82
83     @Inject
84     private RepositorySessionFactory repositorySessionFactory;
85
86     @Inject
87     private RepositoryStatisticsManager repositoryStatisticsManager;
88
89
90     @Inject
91     protected RoleManager roleManager;
92
93     public List<ManagedRepository> getManagedRepositories()
94         throws RepositoryAdminException
95     {
96         List<ManagedRepositoryConfiguration> managedRepoConfigs =
97             getArchivaConfiguration().getConfiguration().getManagedRepositories();
98
99         List<ManagedRepository> managedRepos = new ArrayList<ManagedRepository>( managedRepoConfigs.size() );
100
101         for ( ManagedRepositoryConfiguration repoConfig : managedRepoConfigs )
102         {
103             // TODO add staging repo information back too
104             ManagedRepository repo =
105                 new ManagedRepository( repoConfig.getId(), repoConfig.getName(), repoConfig.getLocation(),
106                                        repoConfig.getLayout(), repoConfig.isSnapshots(), repoConfig.isReleases(),
107                                        repoConfig.isBlockRedeployments(), repoConfig.getRefreshCronExpression(),
108                                        repoConfig.getIndexDir(), repoConfig.isScanned(), repoConfig.getDaysOlder(),
109                                        repoConfig.getRetentionCount(), repoConfig.isDeleteReleasedSnapshots() );
110
111             managedRepos.add( repo );
112         }
113
114         return managedRepos;
115     }
116
117     public Map<String, ManagedRepository> getManagedRepositoriesAsMap()
118         throws RepositoryAdminException
119     {
120         List<ManagedRepository> managedRepositories = getManagedRepositories();
121         Map<String, ManagedRepository> repositoriesMap =
122             new HashMap<String, ManagedRepository>( managedRepositories.size() );
123         for ( ManagedRepository managedRepository : managedRepositories )
124         {
125             repositoriesMap.put( managedRepository.getId(), managedRepository );
126         }
127         return repositoriesMap;
128     }
129
130     public ManagedRepository getManagedRepository( String repositoryId )
131         throws RepositoryAdminException
132     {
133         List<ManagedRepository> repos = getManagedRepositories();
134         for ( ManagedRepository repo : repos )
135         {
136             if ( StringUtils.equals( repo.getId(), repositoryId ) )
137             {
138                 return repo;
139             }
140         }
141         return null;
142     }
143
144     public Boolean addManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
145                                          AuditInformation auditInformation )
146         throws RepositoryAdminException
147     {
148
149         getRepositoryCommonValidator().basicValidation( managedRepository, false );
150         triggerAuditEvent( managedRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
151         return
152             addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
153                                   managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
154                                   managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
155                                   managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
156                                   managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
157                                   managedRepository.isDeleteReleasedSnapshots(), auditInformation,
158                                   getArchivaConfiguration().getConfiguration() ) != null;
159
160     }
161
162     private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
163                                                                  String location, boolean blockRedeployments,
164                                                                  boolean releasesIncluded, boolean snapshotsIncluded,
165                                                                  boolean stageRepoNeeded, String cronExpression,
166                                                                  String indexDir, int daysOlder, int retentionCount,
167                                                                  boolean deteleReleasedSnapshots,
168                                                                  AuditInformation auditInformation,
169                                                                  Configuration config )
170         throws RepositoryAdminException
171     {
172
173         // FIXME : olamy can be empty to avoid scheduled scan ?
174         if ( StringUtils.isNotBlank( cronExpression ) )
175         {
176             CronExpressionValidator validator = new CronExpressionValidator();
177
178             if ( !validator.validate( cronExpression ) )
179             {
180                 throw new RepositoryAdminException( "Invalid cron expression." );
181             }
182         }
183         else
184         {
185             throw new RepositoryAdminException( "Cron expression cannot be empty." );
186         }
187
188         String repoLocation = getRepositoryCommonValidator().removeExpressions( location );
189
190         if ( !GenericValidator.matchRegexp( repoLocation, REPOSITORY_LOCATION_VALID_EXPRESSION ) )
191         {
192             throw new RepositoryAdminException(
193                 "Invalid repository location. Directory must only contain alphanumeric characters, equals(=), question-marks(?), "
194                     + "exclamation-points(!), ampersands(&amp;), forward-slashes(/), back-slashes(\\), underscores(_), dots(.), colons(:), tildes(~), and dashes(-)." );
195         }
196
197         ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration();
198
199         repository.setId( repoId );
200         repository.setBlockRedeployments( blockRedeployments );
201         repository.setReleases( releasesIncluded );
202         repository.setSnapshots( snapshotsIncluded );
203         repository.setName( name );
204         repository.setLocation( repoLocation );
205         repository.setLayout( layout );
206         repository.setRefreshCronExpression( cronExpression );
207         repository.setIndexDir( indexDir );
208         repository.setDaysOlder( daysOlder );
209         repository.setRetentionCount( retentionCount );
210         repository.setDeleteReleasedSnapshots( deteleReleasedSnapshots );
211
212
213         try
214         {
215             addRepository( repository, config );
216             addRepositoryRoles( repository );
217
218             if ( stageRepoNeeded )
219             {
220                 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
221                 addRepository( stagingRepository, config );
222                 addRepositoryRoles( stagingRepository );
223                 triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
224             }
225         }
226         catch ( RoleManagerException e )
227         {
228             throw new RepositoryAdminException( "failed to add repository roles " + e.getMessage(), e );
229         }
230         catch ( IOException e )
231         {
232             throw new RepositoryAdminException( "failed to add repository " + e.getMessage(), e );
233         }
234
235         saveConfiguration( config );
236
237         //MRM-1342 Repository statistics report doesn't appear to be working correctly
238         //scan repository when adding of repository is successful
239         try
240         {
241             scanRepository( repoId, true );
242             // olamy no need of scanning staged repo
243             /*
244             if ( stageRepoNeeded )
245             {
246                 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
247                 scanRepository( stagingRepository.getId(), true );
248             }*/
249         }
250         catch ( Exception e )
251         {
252             log.warn( new StringBuilder( "Unable to scan repository [" ).append( repoId ).append( "]: " ).append(
253                 e.getMessage() ).toString(), e );
254         }
255
256         return repository;
257     }
258
259
260     // FIXME cleanup repositoryGroups when deleting a ManagedRepo
261     public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
262                                             boolean deleteContent )
263         throws RepositoryAdminException
264     {
265         Configuration config = getArchivaConfiguration().getConfiguration();
266
267         ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
268
269         if ( repository == null )
270         {
271             throw new RepositoryAdminException( "A repository with that id does not exist" );
272         }
273
274         triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
275
276         deleteManagedRepository( repository, deleteContent, config, false );
277
278         // stage repo exists ?
279         ManagedRepositoryConfiguration stagingRepository =
280             getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
281         if ( stagingRepository != null )
282         {
283             // do not trigger event when deleting the staged one
284             //triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
285             deleteManagedRepository( stagingRepository, deleteContent, config, true );
286         }
287
288         try
289         {
290             saveConfiguration( config );
291         }
292         catch ( Exception e )
293         {
294             throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
295         }
296
297         return Boolean.TRUE;
298     }
299
300     private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
301                                              Configuration config, boolean stagedOne )
302         throws RepositoryAdminException
303     {
304         if ( !stagedOne )
305         {
306             RepositorySession repositorySession = getRepositorySessionFactory().createSession();
307             try
308             {
309                 MetadataRepository metadataRepository = repositorySession.getRepository();
310                 metadataRepository.removeRepository( repository.getId() );
311                 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
312                 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
313                 repositorySession.save();
314             }
315             catch ( MetadataRepositoryException e )
316             {
317                 throw new RepositoryAdminException( e.getMessage(), e );
318             }
319             finally
320             {
321                 repositorySession.close();
322             }
323         }
324         config.removeManagedRepository( repository );
325
326         if ( deleteContent )
327         {
328             // TODO could be async ? as directory can be huge
329             File dir = new File( repository.getLocation() );
330             if ( !FileUtils.deleteQuietly( dir ) )
331             {
332                 throw new RepositoryAdminException( "Cannot delete repository " + dir );
333             }
334         }
335
336         // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
337         List<ProxyConnectorConfiguration> proxyConnectors =
338             new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
339         for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
340         {
341             if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
342             {
343                 config.removeProxyConnector( proxyConnector );
344             }
345         }
346
347         Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
348         if ( repoToGroupMap != null )
349         {
350             if ( repoToGroupMap.containsKey( repository.getId() ) )
351             {
352                 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
353                 for ( String repoGroup : repoGroups )
354                 {
355                     // copy to prevent UnsupportedOperationException
356                     RepositoryGroupConfiguration repositoryGroupConfiguration =
357                         config.findRepositoryGroupById( repoGroup );
358                     List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
359                     config.removeRepositoryGroup( repositoryGroupConfiguration );
360                     repos.remove( repository.getId() );
361                     repositoryGroupConfiguration.setRepositories( repos );
362                     config.addRepositoryGroup( repositoryGroupConfiguration );
363                 }
364             }
365         }
366
367         try
368         {
369             removeRepositoryRoles( repository );
370         }
371         catch ( RoleManagerException e )
372         {
373             throw new RepositoryAdminException(
374                 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
375         }
376
377         saveConfiguration( config );
378
379         return Boolean.TRUE;
380     }
381
382
383     public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
384                                             AuditInformation auditInformation, boolean resetStats )
385         throws RepositoryAdminException
386     {
387
388         log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
389                    Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
390
391         // Ensure that the fields are valid.
392
393         getRepositoryCommonValidator().basicValidation( managedRepository, true );
394
395         Configuration configuration = getArchivaConfiguration().getConfiguration();
396
397         ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
398
399         if ( toremove != null )
400         {
401             configuration.removeManagedRepository( toremove );
402         }
403
404         ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
405
406         // TODO remove content from old if path has changed !!!!!
407
408         if ( stagingRepository != null )
409         {
410             configuration.removeManagedRepository( stagingRepository );
411         }
412
413         ManagedRepositoryConfiguration managedRepositoryConfiguration =
414             addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
415                                   managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
416                                   managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
417                                   managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
418                                   managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
419                                   managedRepository.isDeleteReleasedSnapshots(), auditInformation,
420                                   getArchivaConfiguration().getConfiguration() );
421
422         // Save the repository configuration.
423         RepositorySession repositorySession = getRepositorySessionFactory().createSession();
424
425         try
426         {
427             triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
428                                auditInformation );
429
430             saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
431             if ( resetStats )
432             {
433                 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
434                 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
435                                                                    managedRepositoryConfiguration.getId() );
436                 repositorySession.save();
437             }
438
439         }
440         catch ( MetadataRepositoryException e )
441         {
442             throw new RepositoryAdminException( e.getMessage(), e );
443         }
444         finally
445         {
446             repositorySession.close();
447         }
448
449         return true;
450     }
451
452     //--------------------------
453     // utils methods
454     //--------------------------
455
456
457     protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
458         throws RepositoryAdminException, IOException
459     {
460         // Normalize the path
461         File file = new File( repository.getLocation() );
462         repository.setLocation( file.getCanonicalPath() );
463         if ( !file.exists() )
464         {
465             file.mkdirs();
466         }
467         if ( !file.exists() || !file.isDirectory() )
468         {
469             throw new RepositoryAdminException(
470                 "Unable to add repository - no write access, can not create the root directory: " + file );
471         }
472
473         configuration.addManagedRepository( repository );
474     }
475
476     private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
477     {
478         ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
479         stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
480         stagingRepository.setLayout( repository.getLayout() );
481         stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
482         stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
483         stagingRepository.setDaysOlder( repository.getDaysOlder() );
484         stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
485         stagingRepository.setIndexDir( repository.getIndexDir() );
486         String path = repository.getLocation();
487         int lastIndex = path.lastIndexOf( '/' );
488         stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
489         stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
490         stagingRepository.setReleases( repository.isReleases() );
491         stagingRepository.setRetentionCount( repository.getRetentionCount() );
492         stagingRepository.setScanned( repository.isScanned() );
493         stagingRepository.setSnapshots( repository.isSnapshots() );
494         return stagingRepository;
495     }
496
497     public Boolean scanRepository( String repositoryId, boolean fullScan )
498     {
499         if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
500         {
501             log.info( "scanning of repository with id {} already scheduled", repositoryId );
502         }
503         RepositoryTask task = new RepositoryTask();
504         task.setRepositoryId( repositoryId );
505         task.setScanAll( fullScan );
506         try
507         {
508             getRepositoryTaskScheduler().queueTask( task );
509         }
510         catch ( TaskQueueException e )
511         {
512             log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
513             return false;
514         }
515         return true;
516     }
517
518     protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
519         throws RoleManagerException
520     {
521         String repoId = newRepository.getId();
522
523         // TODO: double check these are configured on start up
524         // TODO: belongs in the business logic
525
526         if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
527         {
528             getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
529         }
530
531         if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
532         {
533             getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
534         }
535     }
536
537     protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
538         throws RoleManagerException
539     {
540         String repoId = existingRepository.getId();
541
542         if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
543         {
544             getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
545         }
546
547         if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
548         {
549             getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
550         }
551
552         log.debug( "removed user roles associated with repository {}", repoId );
553     }
554
555     //--------------------------
556     // setters/getters
557     //--------------------------
558
559
560     public RoleManager getRoleManager()
561     {
562         return roleManager;
563     }
564
565     public void setRoleManager( RoleManager roleManager )
566     {
567         this.roleManager = roleManager;
568     }
569
570     public RepositoryStatisticsManager getRepositoryStatisticsManager()
571     {
572         return repositoryStatisticsManager;
573     }
574
575     public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
576     {
577         this.repositoryStatisticsManager = repositoryStatisticsManager;
578     }
579
580     public RepositorySessionFactory getRepositorySessionFactory()
581     {
582         return repositorySessionFactory;
583     }
584
585     public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
586     {
587         this.repositorySessionFactory = repositorySessionFactory;
588     }
589
590
591     public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
592     {
593         return repositoryTaskScheduler;
594     }
595
596     public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
597     {
598         this.repositoryTaskScheduler = repositoryTaskScheduler;
599     }
600 }