]> source.dussan.org Git - archiva.git/blob
15d2bc823e1d57ba94856f157d5921efceb6b466
[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     public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
267                                             boolean deleteContent )
268         throws RepositoryAdminException
269     {
270         Configuration config = getArchivaConfiguration().getConfiguration();
271
272         ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
273
274         if ( repository == null )
275         {
276             throw new RepositoryAdminException( "A repository with that id does not exist" );
277         }
278
279         triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
280
281         deleteManagedRepository( repository, deleteContent, config, false );
282
283         // stage repo exists ?
284         ManagedRepositoryConfiguration stagingRepository =
285             getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
286         if ( stagingRepository != null )
287         {
288             // do not trigger event when deleting the staged one
289             deleteManagedRepository( stagingRepository, deleteContent, config, true );
290         }
291
292         try
293         {
294             saveConfiguration( config );
295         }
296         catch ( Exception e )
297         {
298             throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
299         }
300
301         return Boolean.TRUE;
302     }
303
304     private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
305                                              Configuration config, boolean stagedOne )
306         throws RepositoryAdminException
307     {
308
309         try
310         {
311             NexusIndexer nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
312
313             IndexingContext context = nexusIndexer.getIndexingContexts().get( repository.getId() );
314             if ( context != null )
315             {
316                 nexusIndexer.removeIndexingContext( context, deleteContent );
317             }
318         }
319         catch ( PlexusSisuBridgeException e )
320         {
321             throw new RepositoryAdminException( e.getMessage(), e );
322         }
323         catch ( IOException e )
324         {
325             throw new RepositoryAdminException( e.getMessage(), e );
326         }
327         if ( !stagedOne )
328         {
329             RepositorySession repositorySession = getRepositorySessionFactory().createSession();
330             try
331             {
332                 MetadataRepository metadataRepository = repositorySession.getRepository();
333                 metadataRepository.removeRepository( repository.getId() );
334                 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
335                 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
336                 repositorySession.save();
337             }
338             catch ( MetadataRepositoryException e )
339             {
340                 throw new RepositoryAdminException( e.getMessage(), e );
341             }
342             finally
343             {
344                 repositorySession.close();
345             }
346         }
347         config.removeManagedRepository( repository );
348
349         if ( deleteContent )
350         {
351             // TODO could be async ? as directory can be huge
352             File dir = new File( repository.getLocation() );
353             if ( !FileUtils.deleteQuietly( dir ) )
354             {
355                 throw new RepositoryAdminException( "Cannot delete repository " + dir );
356             }
357         }
358
359         // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
360         List<ProxyConnectorConfiguration> proxyConnectors =
361             new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
362         for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
363         {
364             if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
365             {
366                 config.removeProxyConnector( proxyConnector );
367             }
368         }
369
370         Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
371         if ( repoToGroupMap != null )
372         {
373             if ( repoToGroupMap.containsKey( repository.getId() ) )
374             {
375                 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
376                 for ( String repoGroup : repoGroups )
377                 {
378                     // copy to prevent UnsupportedOperationException
379                     RepositoryGroupConfiguration repositoryGroupConfiguration =
380                         config.findRepositoryGroupById( repoGroup );
381                     List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
382                     config.removeRepositoryGroup( repositoryGroupConfiguration );
383                     repos.remove( repository.getId() );
384                     repositoryGroupConfiguration.setRepositories( repos );
385                     config.addRepositoryGroup( repositoryGroupConfiguration );
386                 }
387             }
388         }
389
390         try
391         {
392             removeRepositoryRoles( repository );
393         }
394         catch ( RoleManagerException e )
395         {
396             throw new RepositoryAdminException(
397                 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
398         }
399
400         saveConfiguration( config );
401
402         return Boolean.TRUE;
403     }
404
405
406     public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
407                                             AuditInformation auditInformation, boolean resetStats )
408         throws RepositoryAdminException
409     {
410
411         log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
412                    Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
413
414         // Ensure that the fields are valid.
415
416         getRepositoryCommonValidator().basicValidation( managedRepository, true );
417
418         Configuration configuration = getArchivaConfiguration().getConfiguration();
419
420         ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
421
422         if ( toremove != null )
423         {
424             configuration.removeManagedRepository( toremove );
425         }
426
427         ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
428
429         // TODO remove content from old if path has changed !!!!!
430
431         if ( stagingRepository != null )
432         {
433             configuration.removeManagedRepository( stagingRepository );
434         }
435
436         ManagedRepositoryConfiguration managedRepositoryConfiguration =
437             addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
438                                   managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
439                                   managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
440                                   managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
441                                   managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
442                                   managedRepository.isDeleteReleasedSnapshots(), auditInformation,
443                                   getArchivaConfiguration().getConfiguration() );
444
445         // Save the repository configuration.
446         RepositorySession repositorySession = getRepositorySessionFactory().createSession();
447
448         try
449         {
450             triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
451                                auditInformation );
452
453             saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
454             if ( resetStats )
455             {
456                 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
457                 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
458                                                                    managedRepositoryConfiguration.getId() );
459                 repositorySession.save();
460             }
461
462         }
463         catch ( MetadataRepositoryException e )
464         {
465             throw new RepositoryAdminException( e.getMessage(), e );
466         }
467         finally
468         {
469             repositorySession.close();
470         }
471
472         return true;
473     }
474
475     //--------------------------
476     // utils methods
477     //--------------------------
478
479
480     protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
481         throws RepositoryAdminException, IOException
482     {
483         // Normalize the path
484         File file = new File( repository.getLocation() );
485         repository.setLocation( file.getCanonicalPath() );
486         if ( !file.exists() )
487         {
488             file.mkdirs();
489         }
490         if ( !file.exists() || !file.isDirectory() )
491         {
492             throw new RepositoryAdminException(
493                 "Unable to add repository - no write access, can not create the root directory: " + file );
494         }
495
496         configuration.addManagedRepository( repository );
497     }
498
499     private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
500     {
501         ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
502         stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
503         stagingRepository.setLayout( repository.getLayout() );
504         stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
505         stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
506         stagingRepository.setDaysOlder( repository.getDaysOlder() );
507         stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
508         stagingRepository.setIndexDir( repository.getIndexDir() );
509         String path = repository.getLocation();
510         int lastIndex = path.lastIndexOf( '/' );
511         stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
512         stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
513         stagingRepository.setReleases( repository.isReleases() );
514         stagingRepository.setRetentionCount( repository.getRetentionCount() );
515         stagingRepository.setScanned( repository.isScanned() );
516         stagingRepository.setSnapshots( repository.isSnapshots() );
517         return stagingRepository;
518     }
519
520     public Boolean scanRepository( String repositoryId, boolean fullScan )
521     {
522         if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
523         {
524             log.info( "scanning of repository with id {} already scheduled", repositoryId );
525         }
526         RepositoryTask task = new RepositoryTask();
527         task.setRepositoryId( repositoryId );
528         task.setScanAll( fullScan );
529         try
530         {
531             getRepositoryTaskScheduler().queueTask( task );
532         }
533         catch ( TaskQueueException e )
534         {
535             log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
536             return false;
537         }
538         return true;
539     }
540
541     protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
542         throws RoleManagerException
543     {
544         String repoId = newRepository.getId();
545
546         // TODO: double check these are configured on start up
547         // TODO: belongs in the business logic
548
549         if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
550         {
551             getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
552         }
553
554         if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
555         {
556             getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
557         }
558     }
559
560     protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
561         throws RoleManagerException
562     {
563         String repoId = existingRepository.getId();
564
565         if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
566         {
567             getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
568         }
569
570         if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
571         {
572             getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
573         }
574
575         log.debug( "removed user roles associated with repository {}", repoId );
576     }
577
578     //--------------------------
579     // setters/getters
580     //--------------------------
581
582
583     public RoleManager getRoleManager()
584     {
585         return roleManager;
586     }
587
588     public void setRoleManager( RoleManager roleManager )
589     {
590         this.roleManager = roleManager;
591     }
592
593     public RepositoryStatisticsManager getRepositoryStatisticsManager()
594     {
595         return repositoryStatisticsManager;
596     }
597
598     public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
599     {
600         this.repositoryStatisticsManager = repositoryStatisticsManager;
601     }
602
603     public RepositorySessionFactory getRepositorySessionFactory()
604     {
605         return repositorySessionFactory;
606     }
607
608     public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
609     {
610         this.repositorySessionFactory = repositorySessionFactory;
611     }
612
613
614     public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
615     {
616         return repositoryTaskScheduler;
617     }
618
619     public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
620     {
621         this.repositoryTaskScheduler = repositoryTaskScheduler;
622     }
623 }