]> source.dussan.org Git - archiva.git/blob
293e230e5a8da8686bf09e69ef5b3f3cb24c8ccd
[archiva.git] /
1 package org.apache.archiva.webdav;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.beans.RemoteRepository;
25 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
26 import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin;
27 import org.apache.archiva.audit.AuditEvent;
28 import org.apache.archiva.audit.AuditListener;
29 import org.apache.archiva.audit.Auditable;
30 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
31 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
32 import org.apache.archiva.common.utils.PathUtil;
33 import org.apache.archiva.common.utils.VersionUtil;
34 import org.apache.archiva.configuration.ArchivaConfiguration;
35 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
36 import org.apache.archiva.indexer.merger.IndexMerger;
37 import org.apache.archiva.indexer.merger.IndexMergerException;
38 import org.apache.archiva.indexer.merger.IndexMergerRequest;
39 import org.apache.archiva.indexer.merger.TemporaryGroupIndex;
40 import org.apache.archiva.indexer.search.RepositorySearch;
41 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
42 import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
43 import org.apache.archiva.model.ArchivaRepositoryMetadata;
44 import org.apache.archiva.model.ArtifactReference;
45 import org.apache.archiva.policies.ProxyDownloadException;
46 import org.apache.archiva.proxy.model.RepositoryProxyConnectors;
47 import org.apache.archiva.redback.authentication.AuthenticationException;
48 import org.apache.archiva.redback.authentication.AuthenticationResult;
49 import org.apache.archiva.redback.authorization.AuthorizationException;
50 import org.apache.archiva.redback.authorization.UnauthorizedException;
51 import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
52 import org.apache.archiva.redback.policy.AccountLockedException;
53 import org.apache.archiva.redback.policy.MustChangePasswordException;
54 import org.apache.archiva.redback.system.SecuritySession;
55 import org.apache.archiva.redback.users.User;
56 import org.apache.archiva.redback.users.UserManager;
57 import org.apache.archiva.repository.ManagedRepositoryContent;
58 import org.apache.archiva.repository.RepositoryContentFactory;
59 import org.apache.archiva.repository.RepositoryException;
60 import org.apache.archiva.repository.RepositoryNotFoundException;
61 import org.apache.archiva.repository.content.legacy.LegacyPathParser;
62 import org.apache.archiva.repository.content.maven2.RepositoryRequest;
63 import org.apache.archiva.repository.layout.LayoutException;
64 import org.apache.archiva.repository.metadata.MetadataTools;
65 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
66 import org.apache.archiva.repository.metadata.RepositoryMetadataMerge;
67 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
68 import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler;
69 import org.apache.archiva.security.ServletAuthenticator;
70 import org.apache.archiva.webdav.util.MimeTypes;
71 import org.apache.archiva.webdav.util.RepositoryPathUtil;
72 import org.apache.archiva.webdav.util.TemporaryGroupIndexSessionCleaner;
73 import org.apache.archiva.webdav.util.WebdavMethodUtil;
74 import org.apache.archiva.xml.XMLException;
75 import org.apache.commons.io.FileUtils;
76 import org.apache.commons.io.FilenameUtils;
77 import org.apache.commons.lang.StringUtils;
78 import org.apache.commons.lang.SystemUtils;
79 import org.apache.jackrabbit.webdav.DavException;
80 import org.apache.jackrabbit.webdav.DavResource;
81 import org.apache.jackrabbit.webdav.DavResourceFactory;
82 import org.apache.jackrabbit.webdav.DavResourceLocator;
83 import org.apache.jackrabbit.webdav.DavServletRequest;
84 import org.apache.jackrabbit.webdav.DavServletResponse;
85 import org.apache.jackrabbit.webdav.DavSession;
86 import org.apache.jackrabbit.webdav.lock.LockManager;
87 import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
88 import org.apache.maven.index.context.IndexingContext;
89 import org.codehaus.plexus.digest.ChecksumFile;
90 import org.codehaus.plexus.digest.Digester;
91 import org.codehaus.plexus.digest.DigesterException;
92 import org.slf4j.Logger;
93 import org.slf4j.LoggerFactory;
94 import org.slf4j.MarkerFactory;
95 import org.springframework.context.ApplicationContext;
96 import org.springframework.stereotype.Service;
97
98 import javax.annotation.PostConstruct;
99 import javax.inject.Inject;
100 import javax.inject.Named;
101 import javax.servlet.http.HttpServletResponse;
102 import javax.servlet.http.HttpSession;
103 import java.io.File;
104 import java.io.IOException;
105 import java.util.ArrayList;
106 import java.util.Date;
107 import java.util.HashMap;
108 import java.util.HashSet;
109 import java.util.List;
110 import java.util.Map;
111 import java.util.Set;
112
113 /**
114  *
115  */
116 @Service( "davResourceFactory#archiva" )
117 public class ArchivaDavResourceFactory
118     implements DavResourceFactory, Auditable
119 {
120     private static final String PROXIED_SUFFIX = " (proxied)";
121
122     private static final String HTTP_PUT_METHOD = "PUT";
123
124     private Logger log = LoggerFactory.getLogger( ArchivaDavResourceFactory.class );
125
126     @Inject
127     private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
128
129     @Inject
130     private RepositoryContentFactory repositoryFactory;
131
132     private RepositoryRequest repositoryRequest;
133
134     @Inject
135     @Named( value = "repositoryProxyConnectors#default" )
136     private RepositoryProxyConnectors connectors;
137
138     @Inject
139     private MetadataTools metadataTools;
140
141     @Inject
142     private MimeTypes mimeTypes;
143
144     private ArchivaConfiguration archivaConfiguration;
145
146     @Inject
147     private ServletAuthenticator servletAuth;
148
149     @Inject
150     @Named( value = "httpAuthenticator#basic" )
151     private HttpAuthenticator httpAuth;
152
153     @Inject
154     private RemoteRepositoryAdmin remoteRepositoryAdmin;
155
156     @Inject
157     private ManagedRepositoryAdmin managedRepositoryAdmin;
158
159     @Inject
160     private IndexMerger indexMerger;
161
162     @Inject
163     private RepositorySearch repositorySearch;
164
165     /**
166      * Lock Manager - use simple implementation from JackRabbit
167      */
168     private final LockManager lockManager = new SimpleLockManager();
169
170     private ChecksumFile checksum;
171
172     private Digester digestSha1;
173
174     private Digester digestMd5;
175
176     @Inject
177     @Named( value = "archivaTaskScheduler#repository" )
178     private RepositoryArchivaTaskScheduler scheduler;
179
180     private ApplicationContext applicationContext;
181
182     @Inject
183     public ArchivaDavResourceFactory( ApplicationContext applicationContext, PlexusSisuBridge plexusSisuBridge,
184                                       ArchivaConfiguration archivaConfiguration )
185         throws PlexusSisuBridgeException
186     {
187         this.archivaConfiguration = archivaConfiguration;
188         this.applicationContext = applicationContext;
189         this.checksum = plexusSisuBridge.lookup( ChecksumFile.class );
190
191         this.digestMd5 = plexusSisuBridge.lookup( Digester.class, "md5" );
192         this.digestSha1 = plexusSisuBridge.lookup( Digester.class, "sha1" );
193
194         // TODO remove this hard dependency on maven !!
195         repositoryRequest = new RepositoryRequest( new LegacyPathParser( archivaConfiguration ) );
196     }
197
198     @PostConstruct
199     public void initialize()
200     {
201         // no op
202     }
203
204     public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request,
205                                        final DavServletResponse response )
206         throws DavException
207     {
208         ArchivaDavResourceLocator archivaLocator = checkLocatorIsInstanceOfRepositoryLocator( locator );
209
210         RepositoryGroupConfiguration repoGroupConfig =
211             archivaConfiguration.getConfiguration().getRepositoryGroupsAsMap().get( archivaLocator.getRepositoryId() );
212
213         String activePrincipal = getActivePrincipal( request );
214
215         List<String> resourcesInAbsolutePath = new ArrayList<String>();
216
217         boolean readMethod = WebdavMethodUtil.isReadMethod( request.getMethod() );
218         DavResource resource;
219         if ( repoGroupConfig != null )
220         {
221             if ( !readMethod )
222             {
223                 throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED,
224                                         "Write method not allowed for repository groups." );
225             }
226
227             log.debug( "Repository group '{}' accessed by '{}", repoGroupConfig.getId(), activePrincipal );
228
229             // handle browse requests for virtual repos
230             if ( getLogicalResource( archivaLocator, null, true ).endsWith( "/" ) )
231             {
232                 try
233                 {
234                     return getResourceFromGroup( request, repoGroupConfig.getRepositories(), archivaLocator,
235                                                  repoGroupConfig );
236                 }
237                 catch ( RepositoryAdminException e )
238                 {
239                     throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
240                 }
241             }
242             else
243             {
244                 // make a copy to avoid potential concurrent modifications (eg. by configuration)
245                 // TODO: ultimately, locking might be more efficient than copying in this fashion since updates are
246                 //  infrequent
247                 List<String> repositories = new ArrayList<String>( repoGroupConfig.getRepositories() );
248                 resource = processRepositoryGroup( request, archivaLocator, repositories, activePrincipal,
249                                                    resourcesInAbsolutePath, repoGroupConfig );
250             }
251         }
252         else
253         {
254
255             try
256             {
257                 RemoteRepository remoteRepository =
258                     remoteRepositoryAdmin.getRemoteRepository( archivaLocator.getRepositoryId() );
259
260                 if ( remoteRepository != null )
261                 {
262                     String logicalResource = getLogicalResource( archivaLocator, null, false );
263                     IndexingContext indexingContext = remoteRepositoryAdmin.createIndexContext( remoteRepository );
264                     File resourceFile = StringUtils.equals( logicalResource, "/" )
265                         ? new File( indexingContext.getIndexDirectoryFile().getParent() )
266                         : new File( indexingContext.getIndexDirectoryFile().getParent(), logicalResource );
267                     resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), locator.getResourcePath(), null,
268                                                        request.getRemoteAddr(), activePrincipal,
269                                                        request.getDavSession(), archivaLocator, this, mimeTypes,
270                                                        auditListeners, scheduler );
271                     setHeaders( response, locator, resource );
272                     return resource;
273                 }
274             }
275             catch ( RepositoryAdminException e )
276             {
277                 log.debug( "RepositoryException remote repository with d'{}' not found, msg: {}",
278                            archivaLocator.getRepositoryId(), e.getMessage() );
279             }
280
281             ManagedRepositoryContent managedRepositoryContent = null;
282
283             try
284             {
285                 managedRepositoryContent =
286                     repositoryFactory.getManagedRepositoryContent( archivaLocator.getRepositoryId() );
287             }
288             catch ( RepositoryNotFoundException e )
289             {
290                 throw new DavException( HttpServletResponse.SC_NOT_FOUND,
291                                         "Invalid repository: " + archivaLocator.getRepositoryId() );
292             }
293             catch ( RepositoryException e )
294             {
295                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
296             }
297
298             log.debug( "Managed repository '{}' accessed by '{}'", managedRepositoryContent.getId(), activePrincipal );
299
300             try
301             {
302                 resource = processRepository( request, archivaLocator, activePrincipal, managedRepositoryContent,
303                                               managedRepositoryAdmin.getManagedRepository(
304                                                   archivaLocator.getRepositoryId() ) );
305
306                 String logicalResource = getLogicalResource( archivaLocator, null, false );
307                 resourcesInAbsolutePath.add(
308                     new File( managedRepositoryContent.getRepoRoot(), logicalResource ).getAbsolutePath() );
309
310             }
311             catch ( RepositoryAdminException e )
312             {
313                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
314             }
315         }
316
317         String requestedResource = request.getRequestURI();
318
319         // MRM-872 : merge all available metadata
320         // merge metadata only when requested via the repo group
321         if ( ( repositoryRequest.isMetadata( requestedResource ) || repositoryRequest.isMetadataSupportFile(
322             requestedResource ) ) && repoGroupConfig != null )
323         {
324             // this should only be at the project level not version level!
325             if ( isProjectReference( requestedResource ) )
326             {
327
328                 ArchivaDavResource res = (ArchivaDavResource) resource;
329                 String filePath =
330                     StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ),
331                                                      "/" );
332                 filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml";
333
334                 // for MRM-872 handle checksums of the merged metadata files
335                 if ( repositoryRequest.isSupportFile( requestedResource ) )
336                 {
337                     File metadataChecksum =
338                         new File( filePath + "." + StringUtils.substringAfterLast( requestedResource, "." ) );
339                     if ( metadataChecksum.exists() )
340                     {
341                         LogicalResource logicalResource =
342                             new LogicalResource (getLogicalResource( archivaLocator, null, false ) );
343
344                         resource =
345                             new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), null,
346                                                     request.getRemoteAddr(), activePrincipal, request.getDavSession(),
347                                                     archivaLocator, this, mimeTypes, auditListeners, scheduler );
348                     }
349                 }
350                 else
351                 {
352                     if ( resourcesInAbsolutePath != null && resourcesInAbsolutePath.size() > 1 )
353                     {
354                         // merge the metadata of all repos under group
355                         ArchivaRepositoryMetadata mergedMetadata = new ArchivaRepositoryMetadata();
356                         for ( String resourceAbsPath : resourcesInAbsolutePath )
357                         {
358                             try
359                             {
360                                 File metadataFile = new File( resourceAbsPath );
361                                 ArchivaRepositoryMetadata repoMetadata = MavenMetadataReader.read( metadataFile );
362                                 mergedMetadata = RepositoryMetadataMerge.merge( mergedMetadata, repoMetadata );
363                             }
364                             catch ( XMLException e )
365                             {
366                                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
367                                                         "Error occurred while reading metadata file." );
368                             }
369                             catch ( RepositoryMetadataException r )
370                             {
371                                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
372                                                         "Error occurred while merging metadata file." );
373                             }
374                         }
375
376                         try
377                         {
378                             File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath );
379
380                             LogicalResource logicalResource = new LogicalResource( getLogicalResource( archivaLocator, null, false ) );
381
382                             resource =
383                                 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), null,
384                                                         request.getRemoteAddr(), activePrincipal,
385                                                         request.getDavSession(), archivaLocator, this, mimeTypes,
386                                                         auditListeners, scheduler );
387                         }
388                         catch ( RepositoryMetadataException r )
389                         {
390                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
391                                                     "Error occurred while writing metadata file." );
392                         }
393                         catch ( IOException ie )
394                         {
395                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
396                                                     "Error occurred while generating checksum files." );
397                         }
398                         catch ( DigesterException de )
399                         {
400                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
401                                                     "Error occurred while generating checksum files." + de.getMessage() );
402                         }
403                     }
404                 }
405             }
406         }
407
408         setHeaders( response, locator, resource );
409
410         // compatibility with MRM-440 to ensure browsing the repository works ok
411         if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) )
412         {
413             throw new BrowserRedirectException( resource.getHref() );
414         }
415         resource.addLockManager( lockManager );
416         return resource;
417     }
418
419     private DavResource processRepositoryGroup( final DavServletRequest request,
420                                                 ArchivaDavResourceLocator archivaLocator, List<String> repositories,
421                                                 String activePrincipal, List<String> resourcesInAbsolutePath,
422                                                 RepositoryGroupConfiguration repoGroupConfig )
423         throws DavException
424     {
425         DavResource resource = null;
426         List<DavException> storedExceptions = new ArrayList<DavException>();
427
428         String pathInfo = StringUtils.removeEnd( request.getPathInfo(), "/" );
429
430         String rootPath = StringUtils.substringBeforeLast( pathInfo, "/" );
431
432         if ( StringUtils.endsWith( rootPath, repoGroupConfig.getMergedIndexPath() ) )
433         {
434             // we are in the case of index file request
435             String requestedFileName = StringUtils.substringAfterLast( pathInfo, "/" );
436             File temporaryIndexDirectory =
437                 buildMergedIndexDirectory( repositories, activePrincipal, request, repoGroupConfig );
438
439             File resourceFile = new File( temporaryIndexDirectory, requestedFileName );
440             resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), requestedFileName, null,
441                                                request.getRemoteAddr(), activePrincipal, request.getDavSession(),
442                                                archivaLocator, this, mimeTypes, auditListeners, scheduler );
443
444         }
445         else
446         {
447             for ( String repositoryId : repositories )
448             {
449                 ManagedRepositoryContent managedRepositoryContent;
450                 try
451                 {
452                     managedRepositoryContent = repositoryFactory.getManagedRepositoryContent( repositoryId );
453                 }
454                 catch ( RepositoryNotFoundException e )
455                 {
456                     throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
457                 }
458                 catch ( RepositoryException e )
459                 {
460                     throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
461                 }
462
463                 try
464                 {
465                     ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repositoryId );
466                     DavResource updatedResource =
467                         processRepository( request, archivaLocator, activePrincipal, managedRepositoryContent,
468                                            managedRepository );
469                     if ( resource == null )
470                     {
471                         resource = updatedResource;
472                     }
473
474                     String logicalResource = getLogicalResource( archivaLocator, null, false );
475                     if ( logicalResource.endsWith( "/" ) )
476                     {
477                         logicalResource = logicalResource.substring( 1 );
478                     }
479                     resourcesInAbsolutePath.add(
480                         new File( managedRepositoryContent.getRepoRoot(), logicalResource ).getAbsolutePath() );
481                 }
482                 catch ( DavException e )
483                 {
484                     storedExceptions.add( e );
485                 }
486                 catch ( RepositoryAdminException e )
487                 {
488                     storedExceptions.add( new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ) );
489                 }
490             }
491         }
492         if ( resource == null )
493         {
494             if ( !storedExceptions.isEmpty() )
495             {
496                 // MRM-1232
497                 for ( DavException e : storedExceptions )
498                 {
499                     if ( 401 == e.getErrorCode() )
500                     {
501                         throw e;
502                     }
503                 }
504
505                 throw new DavException( HttpServletResponse.SC_NOT_FOUND );
506             }
507             else
508             {
509                 throw new DavException( HttpServletResponse.SC_NOT_FOUND );
510             }
511         }
512         return resource;
513     }
514
515     private String getLogicalResource( ArchivaDavResourceLocator archivaLocator, ManagedRepository managedRepository,
516                                        boolean useOrigResourcePath )
517     {
518         // FIXME remove this hack
519         // but currently managedRepository can be null in case of group
520         String layout = managedRepository == null ? new ManagedRepository( ).getLayout() : managedRepository.getLayout();
521         RepositoryStorage repositoryStorage =
522             this.applicationContext.getBean( "repositoryStorage#" + layout, RepositoryStorage.class );
523         String path = repositoryStorage.getFilePath(
524             useOrigResourcePath ? archivaLocator.getOrigResourcePath() : archivaLocator.getResourcePath(), managedRepository );
525         log.debug( "found path {} for resourcePath: '{}' with managedRepo '{}' and layout '{}'", path,
526                    archivaLocator.getResourcePath(), managedRepository == null ? "null" : managedRepository.getId(), layout );
527         return path;
528     }
529
530     private DavResource processRepository( final DavServletRequest request, ArchivaDavResourceLocator archivaLocator,
531                                            String activePrincipal, ManagedRepositoryContent managedRepositoryContent,
532                                            ManagedRepository managedRepository )
533         throws DavException
534     {
535         DavResource resource = null;
536         if ( isAuthorized( request, managedRepositoryContent.getId() ) )
537         {
538             String path = getLogicalResource( archivaLocator, managedRepository, false );
539             if ( path.startsWith( "/" ) )
540             {
541                 path = path.substring( 1 );
542             }
543             LogicalResource logicalResource = new LogicalResource( path );
544             File resourceFile = new File( managedRepositoryContent.getRepoRoot(), path );
545             resource =
546                 new ArchivaDavResource( resourceFile.getAbsolutePath(), path, managedRepositoryContent.getRepository(),
547                                         request.getRemoteAddr(), activePrincipal, request.getDavSession(),
548                                         archivaLocator, this, mimeTypes, auditListeners, scheduler );
549
550             if ( WebdavMethodUtil.isReadMethod( request.getMethod() ) )
551             {
552                 if ( archivaLocator.getHref( false ).endsWith( "/" ) && !resourceFile.isDirectory() )
553                 {
554                     // force a resource not found
555                     throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" );
556                 }
557                 else
558                 {
559                     if ( !resource.isCollection() )
560                     {
561                         boolean previouslyExisted = resourceFile.exists();
562
563                         // Attempt to fetch the resource from any defined proxy.
564                         boolean fromProxy =
565                             fetchContentFromProxies( managedRepositoryContent, request, logicalResource );
566
567                         // At this point the incoming request can either be in default or
568                         // legacy layout format.
569                         try
570                         {
571                             // Perform an adjustment of the resource to the managed
572                             // repository expected path.
573                             String localResourcePath =
574                                 repositoryRequest.toNativePath( logicalResource.getPath(), managedRepositoryContent );
575                             resourceFile = new File( managedRepositoryContent.getRepoRoot(), localResourcePath );
576                             resource =
577                                 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
578                                                         managedRepositoryContent.getRepository(),
579                                                         request.getRemoteAddr(), activePrincipal,
580                                                         request.getDavSession(), archivaLocator, this, mimeTypes,
581                                                         auditListeners, scheduler );
582                         }
583                         catch ( LayoutException e )
584                         {
585                             if ( !resourceFile.exists() )
586                             {
587                                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
588                             }
589                         }
590
591                         if ( fromProxy )
592                         {
593                             String event = ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE )
594                                 + PROXIED_SUFFIX;
595
596                             log.debug( "Proxied artifact '{}' in repository '{}' (current user '{}')",
597                                        resourceFile.getName(), managedRepositoryContent.getId(), activePrincipal );
598
599                             triggerAuditEvent( request.getRemoteAddr(), archivaLocator.getRepositoryId(),
600                                                logicalResource.getPath(), event, activePrincipal );
601                         }
602
603                         if ( !resourceFile.exists() )
604                         {
605                             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" );
606                         }
607                     }
608                 }
609             }
610
611             if ( request.getMethod().equals( HTTP_PUT_METHOD ) )
612             {
613                 String resourcePath = logicalResource.getPath();
614
615                 // check if target repo is enabled for releases
616                 // we suppose that release-artifacts can be deployed only to repos enabled for releases
617                 if ( managedRepositoryContent.getRepository().isReleases() && !repositoryRequest.isMetadata(
618                     resourcePath ) && !repositoryRequest.isSupportFile( resourcePath ) )
619                 {
620                     ArtifactReference artifact = null;
621                     try
622                     {
623                         artifact = managedRepositoryContent.toArtifactReference( resourcePath );
624
625                         if ( !VersionUtil.isSnapshot( artifact.getVersion() ) )
626                         {
627                             // check if artifact already exists and if artifact re-deployment to the repository is allowed
628                             if ( managedRepositoryContent.hasContent( artifact )
629                                 && managedRepositoryContent.getRepository().isBlockRedeployments() )
630                             {
631                                 log.warn( "Overwriting released artifacts in repository '{}' is not allowed.",
632                                           managedRepositoryContent.getId() );
633                                 throw new DavException( HttpServletResponse.SC_CONFLICT,
634                                                         "Overwriting released artifacts is not allowed." );
635                             }
636                         }
637                     }
638                     catch ( LayoutException e )
639                     {
640                         log.warn( "Artifact path '{}' is invalid.", resourcePath );
641                     }
642                 }
643
644                 /*
645                  * Create parent directories that don't exist when writing a file This actually makes this
646                  * implementation not compliant to the WebDAV RFC - but we have enough knowledge about how the
647                  * collection is being used to do this reasonably and some versions of Maven's WebDAV don't correctly
648                  * create the collections themselves.
649                  */
650
651                 File rootDirectory = new File( managedRepositoryContent.getRepoRoot() );
652                 File destDir = new File( rootDirectory, logicalResource.getPath() ).getParentFile();
653
654                 if ( !destDir.exists() )
655                 {
656                     destDir.mkdirs();
657                     String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
658
659                     log.debug( "Creating destination directory '{}' (current user '{}')", destDir.getName(),
660                                activePrincipal );
661
662                     triggerAuditEvent( request.getRemoteAddr(), managedRepositoryContent.getId(), relPath,
663                                        AuditEvent.CREATE_DIR, activePrincipal );
664                 }
665             }
666         }
667         return resource;
668     }
669
670     public DavResource createResource( final DavResourceLocator locator, final DavSession davSession )
671         throws DavException
672     {
673         ArchivaDavResourceLocator archivaLocator = checkLocatorIsInstanceOfRepositoryLocator( locator );
674
675         ManagedRepositoryContent managedRepositoryContent;
676         try
677         {
678             managedRepositoryContent =
679                 repositoryFactory.getManagedRepositoryContent( archivaLocator.getRepositoryId() );
680         }
681         catch ( RepositoryNotFoundException e )
682         {
683             throw new DavException( HttpServletResponse.SC_NOT_FOUND,
684                                     "Invalid repository: " + archivaLocator.getRepositoryId() );
685         }
686         catch ( RepositoryException e )
687         {
688             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
689         }
690
691         DavResource resource = null;
692         try
693         {
694             String logicalResource = getLogicalResource( archivaLocator, managedRepositoryAdmin.getManagedRepository(
695                 archivaLocator.getRepositoryId() ), false );
696             if ( logicalResource.startsWith( "/" ) )
697             {
698                 logicalResource = logicalResource.substring( 1 );
699             }
700             File resourceFile = new File( managedRepositoryContent.getRepoRoot(), logicalResource );
701             resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource,
702                                                managedRepositoryContent.getRepository(), davSession, archivaLocator,
703                                                this, mimeTypes, auditListeners, scheduler );
704
705             resource.addLockManager( lockManager );
706         }
707         catch ( RepositoryAdminException e )
708         {
709             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
710         }
711         return resource;
712     }
713
714     private boolean fetchContentFromProxies( ManagedRepositoryContent managedRepository, DavServletRequest request,
715                                              LogicalResource resource )
716         throws DavException
717     {
718         String path = resource.getPath();
719         if ( repositoryRequest.isSupportFile( path ) )
720         {
721             File proxiedFile = connectors.fetchFromProxies( managedRepository, path );
722
723             return ( proxiedFile != null );
724         }
725
726         // Is it a Metadata resource?
727         if ( repositoryRequest.isDefault( path ) && repositoryRequest.isMetadata( path ) )
728         {
729             return connectors.fetchMetatadaFromProxies( managedRepository, path ) != null;
730         }
731
732         // Is it an Archetype Catalog?
733         if ( repositoryRequest.isArchetypeCatalog( path ) )
734         {
735             // FIXME we must implement a merge of remote archetype catalog from remote servers.
736             File proxiedFile = connectors.fetchFromProxies( managedRepository, path );
737
738             return ( proxiedFile != null );
739         }
740
741         // Not any of the above? Then it's gotta be an artifact reference.
742         try
743         {
744             // Get the artifact reference in a layout neutral way.
745             ArtifactReference artifact = repositoryRequest.toArtifactReference( path );
746
747             if ( artifact != null )
748             {
749                 String repositoryLayout = managedRepository.getRepository().getLayout();
750
751                 RepositoryStorage repositoryStorage =
752                     this.applicationContext.getBean( "repositoryStorage#" + repositoryLayout, RepositoryStorage.class );
753                 repositoryStorage.applyServerSideRelocation( managedRepository, artifact );
754
755                 File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact );
756
757                 resource.setPath( managedRepository.toPath( artifact ) );
758
759                 log.debug( "Proxied artifact '{}:{}:{}'", artifact.getGroupId(), artifact.getArtifactId(),
760                            artifact.getVersion() );
761
762                 return ( proxiedFile != null );
763             }
764         }
765         catch ( LayoutException e )
766         {
767             /* eat it */
768         }
769         catch ( ProxyDownloadException e )
770         {
771             log.error( e.getMessage(), e );
772             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
773                                     "Unable to fetch artifact resource." );
774         }
775         return false;
776     }
777
778     // TODO: remove?
779
780     private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action,
781                                     String principal )
782     {
783         AuditEvent event = new AuditEvent( repositoryId, principal, resource, action );
784         event.setRemoteIP( remoteIP );
785
786         for ( AuditListener listener : auditListeners )
787         {
788             listener.auditEvent( event );
789         }
790     }
791
792     public void addAuditListener( AuditListener listener )
793     {
794         this.auditListeners.add( listener );
795     }
796
797     public void clearAuditListeners()
798     {
799         this.auditListeners.clear();
800     }
801
802     public void removeAuditListener( AuditListener listener )
803     {
804         this.auditListeners.remove( listener );
805     }
806
807     private void setHeaders( DavServletResponse response, DavResourceLocator locator, DavResource resource )
808     {
809         // [MRM-503] - Metadata file need Pragma:no-cache response
810         // header.
811         if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" )
812             || ( (ArchivaDavResource) resource ).getLocalResource().isDirectory() )
813         {
814             response.setHeader( "Pragma", "no-cache" );
815             response.setHeader( "Cache-Control", "no-cache" );
816             response.setDateHeader( "Last-Modified", new Date().getTime() );
817         }
818         // if the resource is a directory don't cache it as new groupId deployed will be available
819         // without need of refreshing browser
820         else
821         {
822             // We need to specify this so connecting wagons can work correctly
823             response.setDateHeader( "Last-Modified", resource.getModificationTime() );
824         }
825         // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
826     }
827
828     private ArchivaDavResourceLocator checkLocatorIsInstanceOfRepositoryLocator( DavResourceLocator locator )
829         throws DavException
830     {
831         if ( !( locator instanceof ArchivaDavResourceLocator ) )
832         {
833             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
834                                     "Locator does not implement RepositoryLocator" );
835         }
836
837         // Hidden paths
838         if ( locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) )
839         {
840             throw new DavException( HttpServletResponse.SC_NOT_FOUND );
841         }
842
843         ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator;
844
845         // MRM-419 - Windows Webdav support. Should not 404 if there is no content.
846         if ( StringUtils.isEmpty( archivaLocator.getRepositoryId() ) )
847         {
848             throw new DavException( HttpServletResponse.SC_NO_CONTENT );
849         }
850         return archivaLocator;
851     }
852
853     private static class LogicalResource
854     {
855         private String path;
856
857         public LogicalResource( String path )
858         {
859             this.path = path;
860         }
861
862         public String getPath()
863         {
864             return path;
865         }
866
867         public void setPath( String path )
868         {
869             this.path = path;
870         }
871     }
872
873     protected boolean isAuthorized( DavServletRequest request, String repositoryId )
874         throws DavException
875     {
876         try
877         {
878             AuthenticationResult result = httpAuth.getAuthenticationResult( request, null );
879             SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) );
880
881             return servletAuth.isAuthenticated( request, result ) && servletAuth.isAuthorized( request, securitySession,
882                                                                                                repositoryId,
883                                                                                                WebdavMethodUtil.getMethodPermission(
884                                                                                                    request.getMethod() ) );
885         }
886         catch ( AuthenticationException e )
887         {
888             // safety check for MRM-911
889             String guest = UserManager.GUEST_USERNAME;
890             try
891             {
892                 if ( servletAuth.isAuthorized( guest,
893                                                ( (ArchivaDavResourceLocator) request.getRequestLocator() ).getRepositoryId(),
894                                                WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
895                 {
896                     return true;
897                 }
898             }
899             catch ( UnauthorizedException ae )
900             {
901                 throw new UnauthorizedDavException( repositoryId,
902                                                     "You are not authenticated and authorized to access any repository." );
903             }
904
905             throw new UnauthorizedDavException( repositoryId, "You are not authenticated" );
906         }
907         catch ( MustChangePasswordException e )
908         {
909             throw new UnauthorizedDavException( repositoryId, "You must change your password." );
910         }
911         catch ( AccountLockedException e )
912         {
913             throw new UnauthorizedDavException( repositoryId, "User account is locked." );
914         }
915         catch ( AuthorizationException e )
916         {
917             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
918                                     "Fatal Authorization Subsystem Error." );
919         }
920         catch ( UnauthorizedException e )
921         {
922             throw new UnauthorizedDavException( repositoryId, e.getMessage() );
923         }
924     }
925
926     private DavResource getResourceFromGroup( DavServletRequest request, List<String> repositories,
927                                               ArchivaDavResourceLocator locator,
928                                               RepositoryGroupConfiguration repositoryGroupConfiguration )
929         throws DavException, RepositoryAdminException
930     {
931         List<File> mergedRepositoryContents = new ArrayList<File>();
932         // multiple repo types so we guess they are all the same type
933         // so use the first one
934         // FIXME add a method with group in the repository storage
935         String firstRepoId = repositoryGroupConfiguration.getRepositories().get( 1 );
936
937         String path = getLogicalResource( locator, managedRepositoryAdmin.getManagedRepository( firstRepoId ), false );
938         if ( path.startsWith( "/" ) )
939         {
940             path = path.substring( 1 );
941         }
942         LogicalResource logicalResource = new LogicalResource( path );
943
944         // flow:
945         // if the current user logged in has permission to any of the repositories, allow user to
946         // browse the repo group but displaying only the repositories which the user has permission to access.
947         // otherwise, prompt for authentication.
948
949         String activePrincipal = getActivePrincipal( request );
950
951         boolean allow = isAllowedToContinue( request, repositories, activePrincipal );
952
953         // remove last /
954         String pathInfo = StringUtils.removeEnd( request.getPathInfo(), "/" );
955
956         if ( allow )
957         {
958
959             if ( StringUtils.endsWith( pathInfo, repositoryGroupConfiguration.getMergedIndexPath() ) )
960             {
961                 File mergedRepoDir =
962                     buildMergedIndexDirectory( repositories, activePrincipal, request, repositoryGroupConfiguration );
963                 mergedRepositoryContents.add( mergedRepoDir );
964             }
965             else
966             {
967                 if ( StringUtils.equalsIgnoreCase( pathInfo, "/" + repositoryGroupConfiguration.getId() ) )
968                 {
969                     File tmpDirectory = new File( SystemUtils.getJavaIoTmpDir(),
970                                                   repositoryGroupConfiguration.getId() + "/"
971                                                       + repositoryGroupConfiguration.getMergedIndexPath() );
972                     if ( !tmpDirectory.exists() )
973                     {
974                         synchronized ( tmpDirectory.getAbsolutePath() )
975                         {
976                             if ( !tmpDirectory.exists() )
977                             {
978                                 tmpDirectory.mkdirs();
979                             }
980                         }
981                     }
982                     mergedRepositoryContents.add( tmpDirectory.getParentFile() );
983                 }
984                 for ( String repository : repositories )
985                 {
986                     ManagedRepositoryContent managedRepository = null;
987
988                     try
989                     {
990                         managedRepository = repositoryFactory.getManagedRepositoryContent( repository );
991                     }
992                     catch ( RepositoryNotFoundException e )
993                     {
994                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
995                                                 "Invalid managed repository <" + repository + ">: " + e.getMessage() );
996                     }
997                     catch ( RepositoryException e )
998                     {
999                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1000                                                 "Invalid managed repository <" + repository + ">: " + e.getMessage() );
1001                     }
1002
1003                     File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
1004                     if ( resourceFile.exists() )
1005                     {
1006                         // in case of group displaying index directory doesn't have sense !!
1007                         String repoIndexDirectory = managedRepository.getRepository().getIndexDirectory();
1008                         if ( StringUtils.isNotEmpty( repoIndexDirectory ) )
1009                         {
1010                             if ( !new File( repoIndexDirectory ).isAbsolute() )
1011                             {
1012                                 repoIndexDirectory = new File( managedRepository.getRepository().getLocation(),
1013                                                                StringUtils.isEmpty( repoIndexDirectory )
1014                                                                    ? ".indexer"
1015                                                                    : repoIndexDirectory ).getAbsolutePath();
1016                             }
1017                         }
1018                         if ( StringUtils.isEmpty( repoIndexDirectory ) )
1019                         {
1020                             repoIndexDirectory = new File( managedRepository.getRepository().getLocation(),
1021                                                            ".indexer" ).getAbsolutePath();
1022                         }
1023
1024                         if ( !StringUtils.equals( FilenameUtils.normalize( repoIndexDirectory ),
1025                                                   FilenameUtils.normalize( resourceFile.getAbsolutePath() ) ) )
1026                         {
1027                             // for prompted authentication
1028                             if ( httpAuth.getSecuritySession( request.getSession( true ) ) != null )
1029                             {
1030                                 try
1031                                 {
1032                                     if ( isAuthorized( request, repository ) )
1033                                     {
1034                                         mergedRepositoryContents.add( resourceFile );
1035                                         log.debug( "Repository '{}' accessed by '{}'", repository, activePrincipal );
1036                                     }
1037                                 }
1038                                 catch ( DavException e )
1039                                 {
1040                                     // TODO: review exception handling
1041
1042                                     log.debug( "Skipping repository '{}' for user '{}': {}", managedRepository,
1043                                                activePrincipal, e.getMessage() );
1044
1045                                 }
1046
1047                             }
1048                             else
1049                             {
1050                                 // for the current user logged in
1051                                 try
1052                                 {
1053                                     if ( servletAuth.isAuthorized( activePrincipal, repository,
1054                                                                    WebdavMethodUtil.getMethodPermission(
1055                                                                        request.getMethod() ) ) )
1056                                     {
1057                                         mergedRepositoryContents.add( resourceFile );
1058                                         log.debug( "Repository '{}' accessed by '{}'", repository, activePrincipal );
1059                                     }
1060                                 }
1061                                 catch ( UnauthorizedException e )
1062                                 {
1063                                     // TODO: review exception handling
1064
1065                                     log.debug( "Skipping repository '{}' for user '{}': {}", managedRepository,
1066                                                activePrincipal, e.getMessage() );
1067
1068                                 }
1069                             }
1070                         }
1071                     }
1072                 }
1073             }
1074         }
1075         else
1076         {
1077             throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." );
1078         }
1079
1080         ArchivaVirtualDavResource resource =
1081             new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator,
1082                                            this );
1083
1084         // compatibility with MRM-440 to ensure browsing the repository group works ok
1085         if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) )
1086         {
1087             throw new BrowserRedirectException( resource.getHref() );
1088         }
1089
1090         return resource;
1091     }
1092
1093     protected String getActivePrincipal( DavServletRequest request )
1094     {
1095         User sessionUser = httpAuth.getSessionUser( request.getSession() );
1096         return sessionUser != null ? sessionUser.getUsername() : UserManager.GUEST_USERNAME;
1097     }
1098
1099     /**
1100      * Check if the current user is authorized to access any of the repos
1101      *
1102      * @param request
1103      * @param repositories
1104      * @param activePrincipal
1105      * @return
1106      */
1107     private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal )
1108     {
1109         // when no repositories configured it's impossible to browse nothing !
1110         // at least make possible to see nothing :-)
1111         if ( repositories == null || repositories.isEmpty() )
1112         {
1113             return true;
1114         }
1115
1116         boolean allow = false;
1117
1118         // if securitySession != null, it means that the user was prompted for authentication
1119         if ( httpAuth.getSecuritySession( request.getSession() ) != null )
1120         {
1121             for ( String repository : repositories )
1122             {
1123                 try
1124                 {
1125                     if ( isAuthorized( request, repository ) )
1126                     {
1127                         allow = true;
1128                         break;
1129                     }
1130                 }
1131                 catch ( DavException e )
1132                 {
1133                     continue;
1134                 }
1135             }
1136         }
1137         else
1138         {
1139             for ( String repository : repositories )
1140             {
1141                 try
1142                 {
1143                     if ( servletAuth.isAuthorized( activePrincipal, repository,
1144                                                    WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
1145                     {
1146                         allow = true;
1147                         break;
1148                     }
1149                 }
1150                 catch ( UnauthorizedException e )
1151                 {
1152                     continue;
1153                 }
1154             }
1155         }
1156
1157         return allow;
1158     }
1159
1160     private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename )
1161         throws RepositoryMetadataException, DigesterException, IOException
1162     {
1163         File outputFile = new File( outputFilename );
1164         if ( outputFile.exists() )
1165         {
1166             FileUtils.deleteQuietly( outputFile );
1167         }
1168
1169         outputFile.getParentFile().mkdirs();
1170         RepositoryMetadataWriter.write( mergedMetadata, outputFile );
1171
1172         createChecksumFile( outputFilename, digestSha1 );
1173         createChecksumFile( outputFilename, digestMd5 );
1174
1175         return outputFile;
1176     }
1177
1178     private void createChecksumFile( String path, Digester digester )
1179         throws DigesterException, IOException
1180     {
1181         File checksumFile = new File( path + digester.getFilenameExtension() );
1182         if ( !checksumFile.exists() )
1183         {
1184             FileUtils.deleteQuietly( checksumFile );
1185             checksum.createChecksum( new File( path ), digester );
1186         }
1187         else if ( !checksumFile.isFile() )
1188         {
1189             log.error( "Checksum file is not a file." );
1190         }
1191     }
1192
1193     private boolean isProjectReference( String requestedResource )
1194     {
1195         try
1196         {
1197             metadataTools.toVersionedReference( requestedResource );
1198             return false;
1199         }
1200         catch ( RepositoryMetadataException re )
1201         {
1202             return true;
1203         }
1204     }
1205
1206     protected File buildMergedIndexDirectory( List<String> repositories, String activePrincipal,
1207                                               DavServletRequest request,
1208                                               RepositoryGroupConfiguration repositoryGroupConfiguration )
1209         throws DavException
1210     {
1211
1212         try
1213         {
1214             HttpSession session = request.getSession();
1215
1216             Map<String, TemporaryGroupIndex> temporaryGroupIndexMap =
1217                 (Map<String, TemporaryGroupIndex>) session.getAttribute(
1218                     TemporaryGroupIndexSessionCleaner.TEMPORARY_INDEX_SESSION_KEY );
1219             if ( temporaryGroupIndexMap == null )
1220             {
1221                 temporaryGroupIndexMap = new HashMap<String, TemporaryGroupIndex>();
1222             }
1223
1224             TemporaryGroupIndex tmp = temporaryGroupIndexMap.get( repositoryGroupConfiguration.getId() );
1225
1226             if ( tmp != null && tmp.getDirectory() != null && tmp.getDirectory().exists() )
1227             {
1228                 if ( System.currentTimeMillis() - tmp.getCreationTime() > (
1229                     repositoryGroupConfiguration.getMergedIndexTtl() * 60 * 1000 ) )
1230                 {
1231                     log.debug( MarkerFactory.getMarker( "group.merged.index" ),
1232                                "tmp group index '{}' is too old so delete it", repositoryGroupConfiguration.getId() );
1233                     indexMerger.cleanTemporaryGroupIndex( tmp );
1234                 }
1235                 else
1236                 {
1237                     log.debug( MarkerFactory.getMarker( "group.merged.index" ),
1238                                "merged index for group '{}' found in cache", repositoryGroupConfiguration.getId() );
1239                     return tmp.getDirectory();
1240                 }
1241             }
1242
1243             Set<String> authzRepos = new HashSet<String>();
1244             for ( String repository : repositories )
1245             {
1246                 try
1247                 {
1248                     if ( servletAuth.isAuthorized( activePrincipal, repository,
1249                                                    WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
1250                     {
1251                         authzRepos.add( repository );
1252                         authzRepos.addAll( this.repositorySearch.getRemoteIndexingContextIds( repository ) );
1253                     }
1254                 }
1255                 catch ( UnauthorizedException e )
1256                 {
1257                     // TODO: review exception handling
1258
1259                     log.debug( "Skipping repository '{}' for user '{}': {}", repository, activePrincipal,
1260                                e.getMessage() );
1261                 }
1262             }
1263             log.info( "generate temporary merged index for repository group '{}' for repositories '{}'",
1264                       repositoryGroupConfiguration.getId(), authzRepos );
1265             IndexingContext indexingContext = indexMerger.buildMergedIndex(
1266                 new IndexMergerRequest( authzRepos, true, repositoryGroupConfiguration.getId(),
1267                                         repositoryGroupConfiguration.getMergedIndexPath(),
1268                                         repositoryGroupConfiguration.getMergedIndexTtl() ) );
1269             File mergedRepoDir = indexingContext.getIndexDirectoryFile();
1270             TemporaryGroupIndex temporaryGroupIndex =
1271                 new TemporaryGroupIndex( mergedRepoDir, indexingContext.getId(), repositoryGroupConfiguration.getId(),
1272                                          repositoryGroupConfiguration.getMergedIndexTtl() ).setCreationTime(
1273                     new Date().getTime() );
1274             temporaryGroupIndexMap.put( repositoryGroupConfiguration.getId(), temporaryGroupIndex );
1275             session.setAttribute( TemporaryGroupIndexSessionCleaner.TEMPORARY_INDEX_SESSION_KEY,
1276                                   temporaryGroupIndexMap );
1277             return mergedRepoDir;
1278         }
1279         catch ( RepositoryAdminException e )
1280         {
1281             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
1282         }
1283         catch ( IndexMergerException e )
1284         {
1285             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
1286         }
1287     }
1288
1289
1290     public void setServletAuth( ServletAuthenticator servletAuth )
1291     {
1292         this.servletAuth = servletAuth;
1293     }
1294
1295     public void setHttpAuth( HttpAuthenticator httpAuth )
1296     {
1297         this.httpAuth = httpAuth;
1298     }
1299
1300     public void setScheduler( RepositoryArchivaTaskScheduler scheduler )
1301     {
1302         this.scheduler = scheduler;
1303     }
1304
1305     public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1306     {
1307         this.archivaConfiguration = archivaConfiguration;
1308     }
1309
1310     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1311     {
1312         this.repositoryFactory = repositoryFactory;
1313     }
1314
1315     public void setRepositoryRequest( RepositoryRequest repositoryRequest )
1316     {
1317         this.repositoryRequest = repositoryRequest;
1318     }
1319
1320     public void setConnectors( RepositoryProxyConnectors connectors )
1321     {
1322         this.connectors = connectors;
1323     }
1324
1325     public RemoteRepositoryAdmin getRemoteRepositoryAdmin()
1326     {
1327         return remoteRepositoryAdmin;
1328     }
1329
1330     public void setRemoteRepositoryAdmin( RemoteRepositoryAdmin remoteRepositoryAdmin )
1331     {
1332         this.remoteRepositoryAdmin = remoteRepositoryAdmin;
1333     }
1334
1335     public ManagedRepositoryAdmin getManagedRepositoryAdmin()
1336     {
1337         return managedRepositoryAdmin;
1338     }
1339
1340     public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
1341     {
1342         this.managedRepositoryAdmin = managedRepositoryAdmin;
1343     }
1344 }