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