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