]> source.dussan.org Git - archiva.git/blob
c13f03939c1f5f123b2a725116c7c15bbe2b8875
[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.common.filelock.FileLockException;
23 import org.apache.archiva.common.filelock.FileLockManager;
24 import org.apache.archiva.common.filelock.FileLockTimeoutException;
25 import org.apache.archiva.common.filelock.Lock;
26 import org.apache.archiva.metadata.model.facets.AuditEvent;
27 import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
28 import org.apache.archiva.repository.events.AuditListener;
29 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
30 import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler;
31 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
32 import org.apache.archiva.webdav.util.IndexWriter;
33 import org.apache.archiva.webdav.util.MimeTypes;
34 import org.apache.commons.io.FileUtils;
35 import org.apache.commons.io.IOUtils;
36 import org.apache.jackrabbit.util.Text;
37 import org.apache.jackrabbit.webdav.DavException;
38 import org.apache.jackrabbit.webdav.DavResource;
39 import org.apache.jackrabbit.webdav.DavResourceFactory;
40 import org.apache.jackrabbit.webdav.DavResourceIterator;
41 import org.apache.jackrabbit.webdav.DavResourceIteratorImpl;
42 import org.apache.jackrabbit.webdav.DavResourceLocator;
43 import org.apache.jackrabbit.webdav.DavServletResponse;
44 import org.apache.jackrabbit.webdav.DavSession;
45 import org.apache.jackrabbit.webdav.MultiStatusResponse;
46 import org.apache.jackrabbit.webdav.io.InputContext;
47 import org.apache.jackrabbit.webdav.io.OutputContext;
48 import org.apache.jackrabbit.webdav.lock.ActiveLock;
49 import org.apache.jackrabbit.webdav.lock.LockInfo;
50 import org.apache.jackrabbit.webdav.lock.LockManager;
51 import org.apache.jackrabbit.webdav.lock.Scope;
52 import org.apache.jackrabbit.webdav.lock.Type;
53 import org.apache.jackrabbit.webdav.property.DavProperty;
54 import org.apache.jackrabbit.webdav.property.DavPropertyName;
55 import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
56 import org.apache.jackrabbit.webdav.property.DavPropertySet;
57 import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
58 import org.apache.jackrabbit.webdav.property.ResourceType;
59 import org.joda.time.DateTime;
60 import org.joda.time.format.DateTimeFormatter;
61 import org.joda.time.format.ISODateTimeFormat;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 import javax.servlet.http.HttpServletResponse;
66 import java.io.IOException;
67 import java.io.InputStream;
68 import java.io.OutputStream;
69 import java.nio.file.Files;
70 import java.nio.file.Path;
71 import java.nio.file.Paths;
72 import java.util.ArrayList;
73 import java.util.List;
74 import java.util.stream.Stream;
75
76 /**
77  */
78 public class ArchivaDavResource
79     implements DavResource
80 {
81     public static final String HIDDEN_PATH_PREFIX = ".";
82
83     private final ArchivaDavResourceLocator locator;
84
85     private final DavResourceFactory factory;
86
87     private final Path localResource;
88
89     private final String logicalResource;
90
91     private DavPropertySet properties = null;
92
93     private LockManager lockManager;
94
95     private final DavSession session;
96
97     private String remoteAddr;
98
99     private final org.apache.archiva.repository.ManagedRepository repository;
100
101     private final MimeTypes mimeTypes;
102
103     private List<AuditListener> auditListeners;
104
105     private String principal;
106
107     public static final String COMPLIANCE_CLASS = "1, 2";
108
109     private final ArchivaTaskScheduler scheduler;
110
111     private final FileLockManager fileLockManager;
112
113     private Logger log = LoggerFactory.getLogger( ArchivaDavResource.class );
114
115     public ArchivaDavResource( String localResource, String logicalResource, org.apache.archiva.repository.ManagedRepository repository,
116                                DavSession session, ArchivaDavResourceLocator locator, DavResourceFactory factory,
117                                MimeTypes mimeTypes, List<AuditListener> auditListeners,
118                                RepositoryArchivaTaskScheduler scheduler, FileLockManager fileLockManager )
119     {
120         this.localResource = Paths.get( localResource );
121         this.logicalResource = logicalResource;
122         this.locator = locator;
123         this.factory = factory;
124         this.session = session;
125
126         // TODO: push into locator as well as moving any references out of the resource factory
127         this.repository = repository;
128
129         // TODO: these should be pushed into the repository layer, along with the physical file operations in this class
130         this.mimeTypes = mimeTypes;
131         this.auditListeners = auditListeners;
132         this.scheduler = scheduler;
133         this.fileLockManager = fileLockManager;
134     }
135
136     public ArchivaDavResource( String localResource, String logicalResource, org.apache.archiva.repository.ManagedRepository repository,
137                                String remoteAddr, String principal, DavSession session,
138                                ArchivaDavResourceLocator locator, DavResourceFactory factory, MimeTypes mimeTypes,
139                                List<AuditListener> auditListeners, RepositoryArchivaTaskScheduler scheduler,
140                                FileLockManager fileLockManager )
141     {
142         this( localResource, logicalResource, repository, session, locator, factory, mimeTypes, auditListeners,
143               scheduler, fileLockManager );
144
145         this.remoteAddr = remoteAddr;
146         this.principal = principal;
147     }
148
149     @Override
150     public String getComplianceClass()
151     {
152         return COMPLIANCE_CLASS;
153     }
154
155     @Override
156     public String getSupportedMethods()
157     {
158         return METHODS;
159     }
160
161     @Override
162     public boolean exists()
163     {
164         return Files.exists(localResource);
165     }
166
167     @Override
168     public boolean isCollection()
169     {
170         return Files.isDirectory(localResource);
171     }
172
173     @Override
174     public String getDisplayName()
175     {
176         String resPath = getResourcePath();
177         return ( resPath != null ) ? Text.getName( resPath ) : resPath;
178     }
179
180     @Override
181     public DavResourceLocator getLocator()
182     {
183         return locator;
184     }
185
186     public Path getLocalResource()
187     {
188         return localResource;
189     }
190
191     @Override
192     public String getResourcePath()
193     {
194         return locator.getResourcePath();
195     }
196
197     @Override
198     public String getHref()
199     {
200         return locator.getHref( isCollection() );
201     }
202
203     @Override
204     public long getModificationTime()
205     {
206         try
207         {
208             return Files.getLastModifiedTime(localResource).toMillis();
209         }
210         catch ( IOException e )
211         {
212             log.error("Could not get modification time of {}: {}", localResource, e.getMessage(), e);
213             return 0;
214         }
215     }
216
217     @Override
218     public void spool( OutputContext outputContext )
219         throws IOException
220     {
221         if ( !isCollection() )
222         {
223             outputContext.setContentLength( Files.size( localResource ) );
224             outputContext.setContentType( mimeTypes.getMimeType( localResource.getFileName().toString() ) );
225         }
226
227         try
228         {
229             if ( !isCollection() && outputContext.hasStream() )
230             {
231                 Lock lock = fileLockManager.readFileLock( localResource );
232                 try (InputStream is = Files.newInputStream( lock.getFile()))
233                 {
234                     IOUtils.copy( is, outputContext.getOutputStream() );
235                 }
236             }
237             else if ( outputContext.hasStream() )
238             {
239                 IndexWriter writer = new IndexWriter( this, localResource, logicalResource );
240                 writer.write( outputContext );
241             }
242         }
243         catch ( FileLockException e )
244         {
245             throw new IOException( e.getMessage(), e );
246         }
247         catch ( FileLockTimeoutException e )
248         {
249             throw new IOException( e.getMessage(), e );
250         }
251     }
252
253     @Override
254     public DavPropertyName[] getPropertyNames()
255     {
256         return getProperties().getPropertyNames();
257     }
258
259     @Override
260     public DavProperty getProperty( DavPropertyName name )
261     {
262         return getProperties().get( name );
263     }
264
265     @Override
266     public DavPropertySet getProperties()
267     {
268         return initProperties();
269     }
270
271     @Override
272     public void setProperty( DavProperty property )
273         throws DavException
274     {
275     }
276
277     @Override
278     public void removeProperty( DavPropertyName propertyName )
279         throws DavException
280     {
281     }
282
283     public MultiStatusResponse alterProperties( DavPropertySet setProperties, DavPropertyNameSet removePropertyNames )
284         throws DavException
285     {
286         return null;
287     }
288
289     @SuppressWarnings("unchecked")
290     @Override
291     public MultiStatusResponse alterProperties( List changeList )
292         throws DavException
293     {
294         return null;
295     }
296
297     @Override
298     public DavResource getCollection()
299     {
300         DavResource parent = null;
301         if ( getResourcePath() != null && !getResourcePath().equals( "/" ) )
302         {
303             String parentPath = Text.getRelativeParent( getResourcePath(), 1 );
304             if ( parentPath.equals( "" ) )
305             {
306                 parentPath = "/";
307             }
308             DavResourceLocator parentloc =
309                 locator.getFactory().createResourceLocator( locator.getPrefix(), parentPath );
310             try
311             {
312                 parent = factory.createResource( parentloc, session );
313             }
314             catch ( DavException e )
315             {
316                 // should not occur
317             }
318         }
319         return parent;
320     }
321
322     @Override
323     public void addMember( DavResource resource, InputContext inputContext )
324         throws DavException
325     {
326         Path localFile = localResource.resolve( resource.getDisplayName() );
327         boolean exists = Files.exists(localFile);
328
329         if ( isCollection() && inputContext.hasStream() ) // New File
330         {
331             try (OutputStream stream = Files.newOutputStream( localFile ))
332             {
333                 IOUtils.copy( inputContext.getInputStream(), stream );
334             }
335             catch ( IOException e )
336             {
337                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
338             }
339
340             // TODO: a bad deployment shouldn't delete an existing file - do we need to write to a temporary location first?
341             long expectedContentLength = inputContext.getContentLength();
342             long actualContentLength = 0;
343             try
344             {
345                 actualContentLength = Files.size(localFile);
346             }
347             catch ( IOException e )
348             {
349                 log.error( "Could not get length of file {}: {}", localFile, e.getMessage(), e );
350             }
351             // length of -1 is given for a chunked request or unknown length, in which case we accept what was uploaded
352             if ( expectedContentLength >= 0 && expectedContentLength != actualContentLength )
353             {
354                 String msg = "Content Header length was " + expectedContentLength + " but was " + actualContentLength;
355                 log.debug( "Upload failed: {}", msg );
356
357                 org.apache.archiva.common.utils.FileUtils.deleteQuietly( localFile );
358                 throw new DavException( HttpServletResponse.SC_BAD_REQUEST, msg );
359             }
360
361             queueRepositoryTask( localFile );
362
363             log.debug( "File '{}{}(current user '{}')", resource.getDisplayName(),
364                        ( exists ? "' modified " : "' created " ), this.principal );
365
366             triggerAuditEvent( resource, exists ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE );
367         }
368         else if ( !inputContext.hasStream() && isCollection() ) // New directory
369         {
370             try
371             {
372                 Files.createDirectories( localFile );
373             }
374             catch ( IOException e )
375             {
376                 log.error("Could not create directory {}: {}", localFile, e.getMessage(), e);
377             }
378
379             log.debug( "Directory '{}' (current user '{}')", resource.getDisplayName(), this.principal );
380
381             triggerAuditEvent( resource, AuditEvent.CREATE_DIR );
382         }
383         else
384         {
385             String msg = "Could not write member " + resource.getResourcePath() + " at " + getResourcePath()
386                 + " as this is not a DAV collection";
387             log.debug( msg );
388             throw new DavException( HttpServletResponse.SC_BAD_REQUEST, msg );
389         }
390     }
391
392     @Override
393     public DavResourceIterator getMembers()
394     {
395         List<DavResource> list = new ArrayList<>();
396         if ( exists() && isCollection() )
397         {
398             try ( Stream<Path> stream = Files.list(localResource))
399             {
400                 stream.forEach ( p ->
401                 {
402                     String item = p.toString();
403                     try
404                     {
405                         if ( !item.startsWith( HIDDEN_PATH_PREFIX ) )
406                         {
407                             String path = locator.getResourcePath( ) + '/' + item;
408                             DavResourceLocator resourceLocator =
409                                 locator.getFactory( ).createResourceLocator( locator.getPrefix( ), path );
410                             DavResource resource = factory.createResource( resourceLocator, session );
411
412                             if ( resource != null )
413                             {
414                                 list.add( resource );
415                             }
416                             log.debug( "Resource '{}' retrieved by '{}'", item, this.principal );
417                         }
418                     }
419                     catch ( DavException e )
420                     {
421                         // Should not occur
422                     }
423                 });
424             } catch (IOException e) {
425                 log.error("Error while listing {}", localResource);
426             }
427         }
428         return new DavResourceIteratorImpl( list );
429     }
430
431     @Override
432     public void removeMember( DavResource member )
433         throws DavException
434     {
435         Path resource = checkDavResourceIsArchivaDavResource( member ).getLocalResource();
436
437         if ( Files.exists(resource) )
438         {
439             try
440             {
441                 if ( Files.isDirectory(resource) )
442                 {
443                     org.apache.archiva.common.utils.FileUtils.deleteDirectory( resource );
444                     triggerAuditEvent( member, AuditEvent.REMOVE_DIR );
445                 }
446                 else
447                 {
448                     Files.deleteIfExists( resource );
449                     triggerAuditEvent( member, AuditEvent.REMOVE_FILE );
450                 }
451
452                 log.debug( "{}{}' removed (current user '{}')", ( Files.isDirectory(resource) ? "Directory '" : "File '" ),
453                            member.getDisplayName(), this.principal );
454
455             }
456             catch ( IOException e )
457             {
458                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
459             }
460         }
461         else
462         {
463             throw new DavException( HttpServletResponse.SC_NOT_FOUND );
464         }
465     }
466
467     private void triggerAuditEvent( DavResource member, String action )
468         throws DavException
469     {
470         String path = logicalResource + "/" + member.getDisplayName();
471
472         ArchivaDavResource resource = checkDavResourceIsArchivaDavResource( member );
473         AuditEvent auditEvent = new AuditEvent( locator.getRepositoryId(), resource.principal, path, action );
474         auditEvent.setRemoteIP( resource.remoteAddr );
475
476         for ( AuditListener listener : auditListeners )
477         {
478             listener.auditEvent( auditEvent );
479         }
480     }
481
482     @Override
483     public void move( DavResource destination )
484         throws DavException
485     {
486         if ( !exists() )
487         {
488             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource to copy does not exist." );
489         }
490
491         try
492         {
493             ArchivaDavResource resource = checkDavResourceIsArchivaDavResource( destination );
494             if ( isCollection() )
495             {
496                 FileUtils.moveDirectory( getLocalResource().toFile(), resource.getLocalResource().toFile() );
497
498                 triggerAuditEvent( remoteAddr, locator.getRepositoryId(), logicalResource, AuditEvent.MOVE_DIRECTORY );
499             }
500             else
501             {
502                 FileUtils.moveFile( getLocalResource().toFile(), resource.getLocalResource().toFile() );
503
504                 triggerAuditEvent( remoteAddr, locator.getRepositoryId(), logicalResource, AuditEvent.MOVE_FILE );
505             }
506
507             log.debug( "{}{}' moved to '{}' (current user '{}')", ( isCollection() ? "Directory '" : "File '" ),
508                        getLocalResource().getFileName(), destination, this.principal );
509
510         }
511         catch ( IOException e )
512         {
513             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
514         }
515     }
516
517     @Override
518     public void copy( DavResource destination, boolean shallow )
519         throws DavException
520     {
521         if ( !exists() )
522         {
523             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource to copy does not exist." );
524         }
525
526         if ( shallow && isCollection() )
527         {
528             throw new DavException( DavServletResponse.SC_FORBIDDEN, "Unable to perform shallow copy for collection" );
529         }
530
531         try
532         {
533             ArchivaDavResource resource = checkDavResourceIsArchivaDavResource( destination );
534             if ( isCollection() )
535             {
536                 FileUtils.copyDirectory( getLocalResource().toFile(), resource.getLocalResource().toFile() );
537
538                 triggerAuditEvent( remoteAddr, locator.getRepositoryId(), logicalResource, AuditEvent.COPY_DIRECTORY );
539             }
540             else
541             {
542                 FileUtils.copyFile( getLocalResource().toFile(), resource.getLocalResource().toFile() );
543
544                 triggerAuditEvent( remoteAddr, locator.getRepositoryId(), logicalResource, AuditEvent.COPY_FILE );
545             }
546
547             log.debug( "{}{}' copied to '{}' (current user '{}')", ( isCollection() ? "Directory '" : "File '" ),
548                        getLocalResource().getFileName(), destination, this.principal );
549
550         }
551         catch ( IOException e )
552         {
553             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
554         }
555     }
556
557     @Override
558     public boolean isLockable( Type type, Scope scope )
559     {
560         return Type.WRITE.equals( type ) && Scope.EXCLUSIVE.equals( scope );
561     }
562
563     @Override
564     public boolean hasLock( Type type, Scope scope )
565     {
566         return getLock( type, scope ) != null;
567     }
568
569     @Override
570     public ActiveLock getLock( Type type, Scope scope )
571     {
572         ActiveLock lock = null;
573         if ( exists() && Type.WRITE.equals( type ) && Scope.EXCLUSIVE.equals( scope ) )
574         {
575             lock = lockManager.getLock( type, scope, this );
576         }
577         return lock;
578     }
579
580     @Override
581     public ActiveLock[] getLocks()
582     {
583         ActiveLock writeLock = getLock( Type.WRITE, Scope.EXCLUSIVE );
584         return ( writeLock != null ) ? new ActiveLock[]{ writeLock } : new ActiveLock[0];
585     }
586
587     @Override
588     public ActiveLock lock( LockInfo lockInfo )
589         throws DavException
590     {
591         ActiveLock lock = null;
592         if ( isLockable( lockInfo.getType(), lockInfo.getScope() ) )
593         {
594             lock = lockManager.createLock( lockInfo, this );
595         }
596         else
597         {
598             throw new DavException( DavServletResponse.SC_PRECONDITION_FAILED, "Unsupported lock type or scope." );
599         }
600         return lock;
601     }
602
603     @Override
604     public ActiveLock refreshLock( LockInfo lockInfo, String lockToken )
605         throws DavException
606     {
607         if ( !exists() )
608         {
609             throw new DavException( DavServletResponse.SC_NOT_FOUND );
610         }
611         ActiveLock lock = getLock( lockInfo.getType(), lockInfo.getScope() );
612         if ( lock == null )
613         {
614             throw new DavException( DavServletResponse.SC_PRECONDITION_FAILED,
615                                     "No lock with the given type/scope present on resource " + getResourcePath() );
616         }
617
618         lock = lockManager.refreshLock( lockInfo, lockToken, this );
619
620         return lock;
621     }
622
623     @Override
624     public void unlock( String lockToken )
625         throws DavException
626     {
627         ActiveLock lock = getLock( Type.WRITE, Scope.EXCLUSIVE );
628         if ( lock == null )
629         {
630             throw new DavException( HttpServletResponse.SC_PRECONDITION_FAILED );
631         }
632         else if ( lock.isLockedByToken( lockToken ) )
633         {
634             lockManager.releaseLock( lockToken, this );
635         }
636         else
637         {
638             throw new DavException( DavServletResponse.SC_LOCKED );
639         }
640     }
641
642     @Override
643     public void addLockManager( LockManager lockManager )
644     {
645         this.lockManager = lockManager;
646     }
647
648     @Override
649     public DavResourceFactory getFactory()
650     {
651         return factory;
652     }
653
654     @Override
655     public DavSession getSession()
656     {
657         return session;
658     }
659
660     /**
661      * Fill the set of properties
662      */
663     protected DavPropertySet initProperties()
664     {
665         if ( !exists() )
666         {
667             properties = new DavPropertySet();
668         }
669
670         if ( properties != null )
671         {
672             return properties;
673         }
674
675         DavPropertySet properties = new DavPropertySet();
676
677         // set (or reset) fundamental properties
678         if ( getDisplayName() != null )
679         {
680             properties.add( new DefaultDavProperty( DavPropertyName.DISPLAYNAME, getDisplayName() ) );
681         }
682         if ( isCollection() )
683         {
684             properties.add( new ResourceType( ResourceType.COLLECTION ) );
685             // Windows XP support
686             properties.add( new DefaultDavProperty( DavPropertyName.ISCOLLECTION, "1" ) );
687         }
688         else
689         {
690             properties.add( new ResourceType( ResourceType.DEFAULT_RESOURCE ) );
691
692             // Windows XP support
693             properties.add( new DefaultDavProperty( DavPropertyName.ISCOLLECTION, "0" ) );
694         }
695
696         // Need to get the ISO8601 date for properties
697         DateTime dt = null;
698         try
699         {
700             dt = new DateTime( Files.getLastModifiedTime( localResource ).toMillis() );
701         }
702         catch ( IOException e )
703         {
704             log.error("Could not get modification time of {}: {}", localResource, e.getMessage(), e);
705             dt = new DateTime();
706         }
707         DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
708         String modifiedDate = fmt.print( dt );
709
710         properties.add( new DefaultDavProperty( DavPropertyName.GETLASTMODIFIED, modifiedDate ) );
711
712         properties.add( new DefaultDavProperty( DavPropertyName.CREATIONDATE, modifiedDate ) );
713
714         try
715         {
716             properties.add( new DefaultDavProperty( DavPropertyName.GETCONTENTLENGTH, Files.size(localResource) ) );
717         }
718         catch ( IOException e )
719         {
720             log.error("Could not get file size of {}: {}", localResource, e.getMessage(), e);
721             properties.add( new DefaultDavProperty( DavPropertyName.GETCONTENTLENGTH, 0 ) );
722         }
723
724         this.properties = properties;
725
726         return properties;
727     }
728
729     private ArchivaDavResource checkDavResourceIsArchivaDavResource( DavResource resource )
730         throws DavException
731     {
732         if ( !( resource instanceof ArchivaDavResource ) )
733         {
734             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
735                                     "DavResource is not instance of ArchivaDavResource" );
736         }
737         return (ArchivaDavResource) resource;
738     }
739
740     private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action )
741     {
742         AuditEvent event = new AuditEvent( repositoryId, principal, resource, action );
743         event.setRemoteIP( remoteIP );
744
745         for ( AuditListener listener : auditListeners )
746         {
747             listener.auditEvent( event );
748         }
749     }
750
751     private void queueRepositoryTask( Path localFile )
752     {
753         RepositoryTask task = new RepositoryTask();
754         task.setRepositoryId( repository.getId() );
755         task.setResourceFile( localFile );
756         task.setUpdateRelatedArtifacts( false );
757         task.setScanAll( false );
758
759         try
760         {
761             scheduler.queueTask( task );
762         }
763         catch ( TaskQueueException e )
764         {
765             log.error( "Unable to queue repository task to execute consumers on resource file ['{}"
766                            + "'].", localFile.getFileName() );
767         }
768     }
769 }