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