View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.impl;
18  
19  import java.io.File;
20  import java.lang.reflect.Constructor;
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  import java.net.URL;
24  import java.net.URLStreamHandler;
25  import java.net.URLStreamHandlerFactory;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.commons.vfs2.CacheStrategy;
36  import org.apache.commons.vfs2.Capability;
37  import org.apache.commons.vfs2.FileContentInfoFactory;
38  import org.apache.commons.vfs2.FileName;
39  import org.apache.commons.vfs2.FileObject;
40  import org.apache.commons.vfs2.FileSystem;
41  import org.apache.commons.vfs2.FileSystemConfigBuilder;
42  import org.apache.commons.vfs2.FileSystemException;
43  import org.apache.commons.vfs2.FileSystemManager;
44  import org.apache.commons.vfs2.FileSystemOptions;
45  import org.apache.commons.vfs2.FileType;
46  import org.apache.commons.vfs2.FilesCache;
47  import org.apache.commons.vfs2.NameScope;
48  import org.apache.commons.vfs2.VFS;
49  import org.apache.commons.vfs2.cache.SoftRefFilesCache;
50  import org.apache.commons.vfs2.operations.FileOperationProvider;
51  import org.apache.commons.vfs2.provider.AbstractFileName;
52  import org.apache.commons.vfs2.provider.AbstractFileProvider;
53  import org.apache.commons.vfs2.provider.DefaultURLStreamHandler;
54  import org.apache.commons.vfs2.provider.FileProvider;
55  import org.apache.commons.vfs2.provider.FileReplicator;
56  import org.apache.commons.vfs2.provider.LocalFileProvider;
57  import org.apache.commons.vfs2.provider.TemporaryFileStore;
58  import org.apache.commons.vfs2.provider.UriParser;
59  import org.apache.commons.vfs2.provider.VfsComponent;
60  
61  /**
62   * The default file system manager implementation.
63   */
64  public class DefaultFileSystemManager implements FileSystemManager {
65  
66      /**
67       * This is an internal class because it needs access to the private member providers.
68       */
69      final class VfsStreamHandlerFactory implements URLStreamHandlerFactory {
70          @Override
71          public URLStreamHandler createURLStreamHandler(final String protocol) {
72              final FileProvider provider = providers.get(protocol);
73              if (provider != null) {
74                  return new DefaultURLStreamHandler(context);
75              }
76  
77              // Route all other calls to the default URLStreamHandlerFactory
78              return new URLStreamHandlerProxy();
79          }
80      }
81  
82      /**
83       * Mapping from URI scheme to FileProvider.
84       */
85      private final Map<String, FileProvider> providers = new HashMap<>();
86  
87      /**
88       * List of the schemes of virtual file systems added.
89       */
90      private final List<String> virtualFileSystemSchemes = new ArrayList<>();
91  
92      /**
93       * All components used by this manager.
94       */
95      private final ArrayList<Object> components = new ArrayList<>();
96  
97      /**
98       * The context to pass to providers.
99       */
100     private final DefaultVfsComponentContextntContext.html#DefaultVfsComponentContext">DefaultVfsComponentContext context = new DefaultVfsComponentContext(this);
101 
102     /**
103      * Operations providers added to this manager.
104      */
105     private final Map<String, List<FileOperationProvider>> operationProviders = new HashMap<>();
106 
107     /**
108      * Mappings of file types.
109      */
110     private final FileTypeMapleTypeMap.html#FileTypeMap">FileTypeMap typeMap = new FileTypeMap();
111 
112     /**
113      * The provider for local files.
114      */
115     private LocalFileProvider localFileProvider;
116 
117     /**
118      * The default provider.
119      */
120     private FileProvider defaultProvider;
121 
122     /**
123      * The file replicator to use.
124      */
125     private FileReplicator fileReplicator;
126 
127     /**
128      * The base file to use for relative URI.
129      */
130     private FileObject baseFile;
131 
132     /**
133      * The files cache
134      */
135     private FilesCache filesCache;
136 
137     /**
138      * The cache strategy
139      */
140     private CacheStrategy fileCacheStrategy;
141     /**
142      * Class which decorates all returned fileObjects
143      */
144     private Class<?> fileObjectDecorator;
145 
146     /**
147      * Reflection constructor extracted from {@link #fileObjectDecorator}
148      */
149     private Constructor<?> fileObjectDecoratorConst;
150 
151     /**
152      * The class to use to determine the content-type (mime-type)
153      */
154     private FileContentInfoFactory fileContentInfoFactory;
155 
156     /**
157      * The logger to use. Default implementation.
158      */
159     private Log log = LogFactory.getLog(getClass());
160 
161     /**
162      * The temporary file store to use.
163      */
164     private TemporaryFileStore tempFileStore;
165 
166     /**
167      * The virtual file provider.
168      */
169     private VirtualFileProvider vfsProvider;
170 
171     /**
172      * Flag, if manager is initialized (after init() and before close()).
173      */
174     private boolean init;
175 
176     /**
177      * Closes the given file system.
178      * <p>
179      * If you use VFS as singleton it is VERY dangerous to call this method
180      * </p>
181      *
182      * @param fileSystem The FileSystem to close.
183      */
184     public void _closeFileSystem(final FileSystem fileSystem) {
185         final FileProvider provider = providers.get(fileSystem.getRootName().getScheme());
186         if (provider != null) {
187             ((AbstractFileProvider) provider).closeFileSystem(fileSystem);
188         } else if (fileSystem instanceof VirtualFileSystem) {
189             // vfsProvider does not implement AbstractFileProvider
190             vfsProvider.closeFileSystem(fileSystem);
191         }
192     }
193 
194     /**
195      * Adds an file name extension mapping.
196      *
197      * @param extension The file name extension.
198      * @param scheme The scheme to use for files with this extension.
199      */
200     public void addExtensionMap(final String extension, final String scheme) {
201         typeMap.addExtension(extension, scheme);
202     }
203 
204     /**
205      * Adds a mime type mapping.
206      *
207      * @param mimeType The mime type.
208      * @param scheme The scheme to use for files with this mime type.
209      */
210     public void addMimeTypeMap(final String mimeType, final String scheme) {
211         typeMap.addMimeType(mimeType, scheme);
212     }
213 
214     /**
215      * Adds the specified FileOperationProvider for the specified scheme. Several FileOperationProvider's might be
216      * registered for the same scheme. For example, for "file" scheme we can register SvnWsOperationProvider and
217      * CvsOperationProvider.
218      *
219      * @param scheme The scheme the provider should be registered for.
220      * @param operationProvider The FileOperationProvider.
221      * @throws FileSystemException if an error occurs adding the provider.
222      */
223     @Override
224     public void addOperationProvider(final String scheme, final FileOperationProvider operationProvider)
225             throws FileSystemException {
226         addOperationProvider(new String[] { scheme }, operationProvider);
227     }
228 
229     /**
230      * @see FileSystemManager#addOperationProvider(String, org.apache.commons.vfs2.operations.FileOperationProvider)
231      *
232      * @param schemes The array of schemes the provider should apply to.
233      * @param operationProvider The FileOperationProvider.
234      * @throws FileSystemException if an error occurs.
235      */
236     @Override
237     public void addOperationProvider(final String[] schemes, final FileOperationProvider operationProvider)
238             throws FileSystemException {
239         for (final String scheme : schemes) {
240             if (!operationProviders.containsKey(scheme)) {
241                 final List<FileOperationProvider> providers = new ArrayList<>();
242                 operationProviders.put(scheme, providers);
243             }
244 
245             final List<FileOperationProvider> providers = operationProviders.get(scheme);
246 
247             if (providers.contains(operationProvider)) {
248                 throw new FileSystemException("vfs.operation/operation-provider-already-added.error", scheme);
249             }
250 
251             setupComponent(operationProvider);
252 
253             providers.add(operationProvider);
254         }
255     }
256 
257     /**
258      * Registers a file system provider.
259      * <p>
260      * The manager takes care of all lifecycle management. A provider may be registered multiple times. The first
261      * {@link LocalFileProvider} added will be remembered for {@link #getLocalFileProvider()}.
262      * </p>
263      *
264      * @param urlScheme The scheme the provider will handle.
265      * @param provider The provider.
266      * @throws FileSystemException if an error occurs adding the provider.
267      */
268     public void addProvider(final String urlScheme, final FileProvider provider) throws FileSystemException {
269         addProvider(new String[] { urlScheme }, provider);
270     }
271 
272     /**
273      * Registers a file system provider.
274      * <p>
275      * The manager takes care of all lifecycle management. A provider may be registered multiple times. The first
276      * {@link LocalFileProvider} added will be remembered for {@link #getLocalFileProvider()}.
277      * </p>
278      *
279      * @param urlSchemes The schemes the provider will handle.
280      * @param provider The provider.
281      * @throws FileSystemException if an error occurs adding the provider.
282      */
283     public void addProvider(final String[] urlSchemes, final FileProvider provider) throws FileSystemException {
284         // fail duplicate schemes
285         for (final String scheme : urlSchemes) {
286             if (providers.containsKey(scheme)) {
287                 throw new FileSystemException("vfs.impl/multiple-providers-for-scheme.error", scheme);
288             }
289         }
290 
291         // Contextualise the component (if not already)
292         setupComponent(provider);
293 
294         // Add to map
295         Arrays.stream(urlSchemes).forEach(scheme -> providers.put(scheme, provider));
296 
297         if (provider instanceof LocalFileProvider && localFileProvider == null) {
298             localFileProvider = (LocalFileProvider) provider;
299         }
300     }
301 
302     protected void addVirtualFileSystemScheme(String rootUri) {
303         if (rootUri.indexOf(':') != -1) {
304             rootUri = rootUri.substring(0, rootUri.indexOf(':'));
305         }
306         virtualFileSystemSchemes.add(rootUri);
307     }
308 
309     /**
310      * Determines if a layered file system can be created for a given file.
311      *
312      * @param file The file to check for.
313      * @return true if the FileSystem can be created.
314      * @throws FileSystemException if an error occurs.
315      */
316     @Override
317     public boolean canCreateFileSystem(final FileObject file) throws FileSystemException {
318         return typeMap.getScheme(file) != null;
319     }
320 
321     /**
322      * Closes the manager.
323      * <p>
324      * This will close all providers (all files), it will also close all managed components including temporary files,
325      * replicator, file cache and file operations.
326      * </p>
327      * <p>
328      * The manager is in uninitialized state after this method.
329      * </p>
330      */
331     @Override
332     public void close() {
333         if (!init) {
334             return;
335         }
336 
337         // make sure all discovered components in
338         // org.apache.commons.vfs2.impl.StandardFileSystemManager.configure(Element)
339         // are closed here
340 
341         // Close the file system providers.
342         providers.values().forEach(this::closeComponent);
343 
344         // Close the other components
345         closeComponent(vfsProvider);
346         closeComponent(fileReplicator);
347         closeComponent(tempFileStore);
348         closeComponent(defaultProvider);
349 
350         // unregister all providers here, so if any components have local file references
351         // they can still resolve against the supported schemes
352         providers.clear();
353 
354         // FileOperations are components, too
355         operationProviders.values().forEach(opProviders -> opProviders.forEach(this::closeComponent));
356 
357         // unregister all
358         operationProviders.clear();
359 
360         // collections with add()
361         typeMap.clear();
362 
363         // Close cache last.
364         closeComponent(filesCache);
365 
366         // should not happen, but make debugging easier:
367         if (!components.isEmpty()) {
368             log.warn("DefaultFilesystemManager.close: not all components are closed: " + components.toString());
369         }
370         components.clear();
371 
372         // managed components
373         vfsProvider = null;
374 
375         // virtual schemas
376         virtualFileSystemSchemes.clear();
377 
378         // setters and derived state
379         defaultProvider = null;
380         baseFile = null;
381         fileObjectDecorator = null;
382         fileObjectDecoratorConst = null;
383         localFileProvider = null;
384         fileReplicator = null;
385         tempFileStore = null;
386         // setters with init() defaults
387         filesCache = null;
388         fileCacheStrategy = null;
389         fileContentInfoFactory = null;
390 
391         init = false;
392     }
393 
394     /**
395      * Closes a component, if it has not already been closed.
396      *
397      * @param component The component to close.
398      */
399     private void closeComponent(final Object component) {
400         if (component != null && components.contains(component)) {
401             if (component instanceof VfsComponent) {
402                 ((VfsComponent) component).close();
403             }
404             components.remove(component);
405         }
406     }
407 
408     /**
409      * Closes the given file system.
410      * <p>
411      * If you use VFS as singleton it is VERY dangerous to call this method.
412      * </p>
413      *
414      * @param fileSystem The FileSystem to close.
415      */
416     @Override
417     public void closeFileSystem(final FileSystem fileSystem) {
418         // inform the cache ...
419         getFilesCache().clear(fileSystem);
420 
421         // just in case the cache didnt call _closeFileSystem
422         _closeFileSystem(fileSystem);
423     }
424 
425     /**
426      * Creates a layered file system.
427      *
428      * @param file The FileObject to use.
429      * @return The layered FileObject.
430      * @throws FileSystemException if an error occurs.
431      */
432     @Override
433     public FileObject#FileObject">FileObject createFileSystem(final FileObject file) throws FileSystemException {
434         final String scheme = typeMap.getScheme(file);
435         FileSystemException.requireNonNull(scheme, "vfs.impl/no-provider-for-file.error", file);
436         return createFileSystem(scheme, file);
437     }
438 
439     /**
440      * Creates a layered file system.
441      *
442      * @param scheme The scheme to use.
443      * @param file The FileObject.
444      * @return The layered FileObject.
445      * @throws FileSystemException if an error occurs.
446      */
447     @Override
448     public FileObject createFileSystem(FileObject="jxr_keyword">final String scheme, final FileObject file) throws FileSystemException {
449         final FileProvider provider = providers.get(scheme);
450         FileSystemException.requireNonNull(provider, "vfs.impl/unknown-provider.error", scheme, file);
451         return provider.createFileSystem(scheme, file, file.getFileSystem().getFileSystemOptions());
452     }
453 
454     /**
455      * Creates a virtual file system.
456      *
457      * @param rootFile The FileObject to use.
458      * @return The FileObject in the VirtualFileSystem.
459      * @throws FileSystemException if an error occurs creating the file.
460      */
461     @Override
462     public FileObjectject">FileObject createVirtualFileSystem(final FileObject rootFile) throws FileSystemException {
463         final FileObject fileObject = vfsProvider.createFileSystem(rootFile);
464         addVirtualFileSystemScheme(rootFile.getName().getScheme());
465         return fileObject;
466     }
467 
468     /**
469      * Creates an empty virtual file system.
470      *
471      * @param rootUri The URI to use as the root of the FileSystem.
472      * @return A FileObject in the virtual FileSystem.
473      * @throws FileSystemException if an error occurs.
474      */
475     @Override
476     public FileObject createVirtualFileSystem(final String rootUri) throws FileSystemException {
477         final FileObject fileObject = vfsProvider.createFileSystem(rootUri);
478         addVirtualFileSystemScheme(rootUri);
479         return fileObject;
480     }
481 
482     /**
483      * Free all resources used by unused file systems created by this manager.
484      */
485     public void freeUnusedResources() {
486         if (!init) {
487             return;
488         }
489 
490         // Close the providers.
491         for (final FileProvider fileProvider : providers.values()) {
492             if (fileProvider instanceof AbstractFileProvider) {
493                 ((AbstractFileProvider) fileProvider).freeUnusedResources();
494             }
495         }
496         // vfsProvider does not need to free resources
497     }
498 
499     /**
500      * Returns the base file used to resolve relative URI.
501      *
502      * @return The FileObject that represents the base file.
503      * @throws FileSystemException if an error occurs.
504      */
505     @Override
506     public FileObject getBaseFile() throws FileSystemException {
507         return baseFile;
508     }
509 
510     /**
511      * Get the cache strategy used.
512      *
513      * @return The CacheStrategy.
514      */
515     @Override
516     public CacheStrategy getCacheStrategy() {
517         return fileCacheStrategy;
518     }
519 
520     /**
521      * get the fileContentInfoFactory used to determine the infos of a file content.
522      *
523      * @return The FileContentInfoFactory.
524      */
525     @Override
526     public FileContentInfoFactory getFileContentInfoFactory() {
527         return fileContentInfoFactory;
528     }
529 
530     /**
531      * Get the file object decorator used.
532      *
533      * @return The decorator.
534      */
535     @Override
536     public Class<?> getFileObjectDecorator() {
537         return fileObjectDecorator;
538     }
539 
540     /**
541      * The constructor associated to the fileObjectDecorator. We cache it here for performance reasons.
542      *
543      * @return The decorator's Constructor.
544      */
545     @Override
546     public Constructor<?> getFileObjectDecoratorConst() {
547         return fileObjectDecoratorConst;
548     }
549 
550     /**
551      * Returns the filesCache implementation used to cache files.
552      *
553      * @return The FilesCache.
554      */
555     @Override
556     public FilesCache getFilesCache() {
557         return filesCache;
558     }
559 
560     /**
561      * Get the configuration builder for the given scheme.
562      *
563      * @param scheme The scheme to locate.
564      * @return The FileSystemConfigBuilder for the scheme.
565      * @throws FileSystemException if the given scheme is not konwn
566      */
567     @Override
568     public FileSystemConfigBuilder getFileSystemConfigBuilder(final String scheme) throws FileSystemException {
569         final FileProvider provider = providers.get(scheme);
570         FileSystemException.requireNonNull(provider, "vfs.impl/unknown-scheme.error", scheme);
571         return provider.getConfigBuilder();
572     }
573 
574     /**
575      * Locates the local file provider.
576      * <p>
577      * The local file provider is the first {@linkplain #addProvider(String[], FileProvider) provider added}
578      * implementing {@link LocalFileProvider}.
579      * </p>
580      *
581      * @return The LocalFileProvider.
582      * @throws FileSystemException if no local file provider was set.
583      */
584     private LocalFileProvider getLocalFileProvider() throws FileSystemException {
585         return FileSystemException.requireNonNull(localFileProvider, "vfs.impl/no-local-file-provider.error");
586     }
587 
588     /**
589      * Returns the logger used by this manager.
590      *
591      * @return the Logger.
592      */
593     protected Log getLogger() {
594         return log;
595     }
596 
597     /**
598      * @param scheme the scheme for wich we want to get the list af registered providers.
599      *
600      * @return the registered FileOperationProviders for the specified scheme. If there were no providers registered for
601      *         the scheme, it returns null.
602      *
603      * @throws FileSystemException if an error occurs.
604      */
605     @Override
606     public FileOperationProvider[] getOperationProviders(final String scheme) throws FileSystemException {
607 
608         final List<?> providers = operationProviders.get(scheme);
609         if (providers == null || providers.isEmpty()) {
610             return null;
611         }
612         return providers.toArray(FileOperationProvider.EMPTY_ARRAY);
613     }
614 
615     /**
616      * Get the capabilities for a given scheme.
617      *
618      * @param scheme The scheme to located.
619      * @return A Collection of capabilities.
620      * @throws FileSystemException if the given scheme is not konwn
621      */
622     @Override
623     public Collection<Capability> getProviderCapabilities(final String scheme) throws FileSystemException {
624         final FileProvider provider = providers.get(scheme);
625         FileSystemException.requireNonNull(provider, "vfs.impl/unknown-scheme.error", scheme);
626         return provider.getCapabilities();
627     }
628 
629     /**
630      * Returns the file replicator.
631      *
632      * @return The file replicator. Never returns null.
633      * @throws FileSystemException if there is no FileReplicator.
634      */
635     public FileReplicator getReplicator() throws FileSystemException {
636         return FileSystemException.requireNonNull(fileReplicator, "vfs.impl/no-replicator.error");
637     }
638 
639     /**
640      * Get the schemes currently available.
641      *
642      * @return The array of scheme names.
643      */
644     @Override
645     public String[] getSchemes() {
646         final List<String> schemes = new ArrayList<>(providers.size() + virtualFileSystemSchemes.size());
647         schemes.addAll(providers.keySet());
648         schemes.addAll(virtualFileSystemSchemes);
649         return schemes.toArray(new String[]{});
650     }
651 
652     /**
653      * Returns the temporary file store.
654      *
655      * @return The file store. Never returns null.
656      * @throws FileSystemException if there is no TemporaryFileStore.
657      */
658     public TemporaryFileStore getTemporaryFileStore() throws FileSystemException {
659         return FileSystemException.requireNonNull(tempFileStore, "vfs.impl/no-temp-file-store.error");
660     }
661 
662     /**
663      * Get the URLStreamHandlerFactory.
664      *
665      * @return The URLStreamHandlerFactory.
666      */
667     @Override
668     public URLStreamHandlerFactory getURLStreamHandlerFactory() {
669         return new VfsStreamHandlerFactory();
670     }
671 
672     /**
673      * Returns true if this manager has a provider for a particular scheme.
674      *
675      * @param scheme The scheme to check.
676      * @return true if a provider is configured for this scheme, false otherwise.
677      */
678     @Override
679     public boolean hasProvider(final String scheme) {
680         return providers.containsKey(scheme);
681     }
682 
683     /**
684      * Initializes this manager.
685      * <p>
686      * If no value for the following properties was specified, it will use the following defaults:
687      * </p>
688      * <ul>
689      * <li>fileContentInfoFactory = new FileContentInfoFilenameFactory()</li>
690      * <li>filesCache = new SoftRefFilesCache()</li>
691      * <li>fileCacheStrategy = CacheStrategy.ON_RESOLVE</li>
692      * </ul>
693      *
694      * @throws FileSystemException if an error occurs during initialization.
695      */
696     public void init() throws FileSystemException {
697         if (fileContentInfoFactory == null) {
698             fileContentInfoFactory = new FileContentInfoFilenameFactory();
699         }
700 
701         if (filesCache == null) {
702             filesCache = new SoftRefFilesCache();
703         }
704         if (fileCacheStrategy == null) {
705             fileCacheStrategy = CacheStrategy.ON_RESOLVE;
706         }
707         setupComponent(filesCache);
708 
709         vfsProvider = new VirtualFileProvider();
710         setupComponent(vfsProvider);
711 
712         init = true;
713     }
714 
715     /**
716      * Unregisters a file system provider.
717      *
718      * @param urlScheme The scheme of the provider.
719      * @since 2.8.0
720      */
721     public void removeProvider(final String urlScheme) {
722         final FileProvider provider = providers.remove(urlScheme);
723         // check whether the same instance is not used somewhere else
724         if (provider != null && !providers.containsValue(provider)) {
725             closeComponent(provider);
726         }
727     }
728 
729     /**
730      * Resolves a URI, relative to base file.
731      * <p>
732      * Uses the {@linkplain #getLocalFileProvider() local file provider} to locate the system file.
733      *
734      * @param baseFile The base File to use to locate the file.
735      * @param uri The URI of the file to locate.
736      * @return The FileObject for the located file.
737      * @throws FileSystemException if the file cannot be located or an error occurs.
738      */
739     @Override
740     public FileObject resolveFile(final File baseFile, final String uri) throws FileSystemException {
741         return resolveFile(getLocalFileProvider().findLocalFile(baseFile), uri);
742     }
743 
744     /**
745      * Resolves a URI, relative to a base file.
746      *
747      * @param baseFile The base FileOjbect to use to locate the file.
748      * @param uri The URI of the file to locate.
749      * @return The FileObject for the located file.
750      * @throws FileSystemException if the file cannot be located or an error occurs.
751      */
752     @Override
753     public FileObject.html#FileObject">FileObject resolveFile(final FileObject baseFile, final String uri) throws FileSystemException {
754         return resolveFile(baseFile, uri, baseFile == null ? null : baseFile.getFileSystem().getFileSystemOptions());
755     }
756 
757     /**
758      * Resolves a URI, relative to a base file with specified FileSystem configuration.
759      *
760      * @param baseFile The base file.
761      * @param uri The file name. May be a fully qualified or relative path or a url.
762      * @param fileSystemOptions Options to pass to the file system.
763      * @return A FileObject representing the target file.
764      * @throws FileSystemException if an error occurs accessing the file.
765      */
766     public FileObject.html#FileObject">FileObject resolveFile(final FileObject baseFile, final String uri,
767             final FileSystemOptions fileSystemOptions) throws FileSystemException {
768         final FileObject realBaseFile;
769         if (baseFile != null && VFS.isUriStyle() && baseFile.getName().isFile()) {
770             realBaseFile = baseFile.getParent();
771         } else {
772             realBaseFile = baseFile;
773         }
774         // TODO: use resolveName and use this name to resolve the fileObject
775 
776         UriParser.checkUriEncoding(uri);
777 
778         if (uri == null) {
779             throw new IllegalArgumentException();
780         }
781 
782         // Extract the scheme
783         final String scheme = UriParser.extractScheme(getSchemes(), uri);
784         if (scheme != null) {
785             // An absolute URI - locate the provider
786             final FileProvider provider = providers.get(scheme);
787             if (provider != null) {
788                 return provider.findFile(realBaseFile, uri, fileSystemOptions);
789             }
790             // Otherwise, assume a local file
791         }
792 
793         // Handle absolute file names
794         if (localFileProvider != null && localFileProvider.isAbsoluteLocalName(uri)) {
795             return localFileProvider.findLocalFile(uri);
796         }
797 
798         if (scheme != null) {
799             // An unknown scheme - hand it to the default provider
800             FileSystemException.requireNonNull(defaultProvider, "vfs.impl/unknown-scheme.error", scheme, uri);
801             return defaultProvider.findFile(realBaseFile, uri, fileSystemOptions);
802         }
803 
804         // Assume a relative name - use the supplied base file
805         FileSystemException.requireNonNull(realBaseFile, "vfs.impl/find-rel-file.error", uri);
806 
807         return realBaseFile.resolveFile(uri);
808     }
809 
810     /**
811      * Locates a file by URI.
812      *
813      * @param uri The URI of the file to locate.
814      * @return The FileObject for the located file.
815      * @throws FileSystemException if the file cannot be located or an error occurs.
816      */
817     @Override
818     public FileObject resolveFile(final String uri) throws FileSystemException {
819         return resolveFile(getBaseFile(), uri);
820     }
821 
822     /**
823      * Locate a file by URI, use the FileSystemOptions for file-system creation.
824      *
825      * @param uri The URI of the file to locate.
826      * @param fileSystemOptions The options for the FileSystem.
827      * @return The FileObject for the located file.
828      * @throws FileSystemException if the file cannot be located or an error occurs.
829      */
830 
831     @Override
832     public FileObject resolveFile(final String uri, final FileSystemOptions fileSystemOptions)
833             throws FileSystemException {
834         // return resolveFile(baseFile, uri, fileSystemOptions);
835         return resolveFile(getBaseFile(), uri, fileSystemOptions);
836     }
837 
838     /**
839      * Converts a URI into a {@link FileObject}.
840      *
841      * @param uri The URI to convert.
842      * @return The {@link FileObject} that represents the URI. Never returns null.
843      * @throws FileSystemException On error converting the URI.
844      * @since 2.1
845      */
846     @Override
847     public FileObject resolveFile(final URI uri) throws FileSystemException {
848         // TODO Push the URI deeper into VFS
849         return resolveFile(baseFile, uri.toString(), null);
850     }
851 
852     /**
853      * Converts a URL into a {@link FileObject}.
854      *
855      * @param url The URL to convert.
856      * @return The {@link FileObject} that represents the URL. Never returns null.
857      * @throws FileSystemException On error converting the URL.
858      * @since 2.1
859      */
860     @Override
861     public FileObject resolveFile(final URL url) throws FileSystemException {
862         try {
863             return this.resolveFile(url.toURI());
864         } catch (final URISyntaxException e) {
865             throw new FileSystemException(e);
866         }
867     }
868 
869     /**
870      * Resolves a name, relative to the file. If the supplied name is an absolute path, then it is resolved relative to
871      * the root of the file system that the file belongs to. If a relative name is supplied, then it is resolved
872      * relative to this file name.
873      *
874      * @param root The base FileName.
875      * @param path The path to the file relative to the base FileName or an absolute path.
876      * @return The constructed FileName.
877      * @throws FileSystemException if an error occurs constructing the FileName.
878      */
879     @Override
880     public FileName.html#FileName">FileName resolveName(final FileName root, final String path) throws FileSystemException {
881         return resolveName(root, path, NameScope.FILE_SYSTEM);
882     }
883 
884 
885     /**
886      * Resolves a name, relative to the root.
887      *
888      * @param base the base file name
889      * @param name the name
890      * @param scope the {@link NameScope}
891      * @return The FileName of the file.
892      * @throws FileSystemException if an error occurs.
893      */
894     @Override
895     public FileName.html#FileName">FileName resolveName(final FileName base, final String name, final NameScope scope)
896             throws FileSystemException {
897         FileSystemException.requireNonNull(base, "Invalid base FileName.");
898         FileSystemException.requireNonNull(name, "Invalid name FileName.");
899         final FileName realBase;
900         if (VFS.isUriStyle() && base.isFile()) {
901             realBase = base.getParent();
902         } else {
903             realBase = base;
904         }
905 
906         final StringBuilder buffer = new StringBuilder(name);
907 
908         // Adjust separators
909         UriParser.fixSeparators(buffer);
910         String scheme = UriParser.extractScheme(getSchemes(), buffer.toString());
911 
912         // Determine whether to prepend the base path
913         if (name.isEmpty() || (scheme == null && buffer.charAt(0) != FileName.SEPARATOR_CHAR)) {
914             // Supplied path is not absolute
915             if (!VFS.isUriStyle()) {
916                 // when using URIs the parent already do have the trailing "/"
917                 buffer.insert(0, FileName.SEPARATOR_CHAR);
918             }
919             buffer.insert(0, realBase.getPath());
920         }
921 
922         // Normalise the path
923         final FileType fileType = UriParser.normalisePath(buffer);
924 
925         // Check the name is ok
926         final String resolvedPath = buffer.toString();
927         if (!AbstractFileName.checkName(realBase.getPath(), resolvedPath, scope)) {
928             throw new FileSystemException("vfs.provider/invalid-descendent-name.error", name);
929         }
930 
931         final String fullPath;
932         if (scheme != null) {
933             fullPath = resolvedPath;
934         } else {
935             scheme = realBase.getScheme();
936             fullPath = realBase.getRootURI() + resolvedPath;
937         }
938         final FileProvider provider = providers.get(scheme);
939         if (provider != null) {
940             // TODO: extend the file name parser to be able to parse
941             // only a pathname and take the missing informations from
942             // the base. Then we can get rid of the string operation.
943             // // String fullPath = base.getRootURI() +
944             // resolvedPath.substring(1);
945 
946             return provider.parseUri(realBase, fullPath);
947         }
948 
949         // An unknown scheme - hand it to the default provider - if possible
950         if (scheme != null && defaultProvider != null) {
951             return defaultProvider.parseUri(realBase, fullPath);
952         }
953 
954         // TODO: avoid fallback to this point
955         // this happens if we have a virtual filesystem (no provider for scheme)
956         return ((AbstractFileName) realBase).createName(resolvedPath, fileType);
957     }
958 
959     /**
960      * Resolve the uri to a file name.
961      *
962      * @param uri The URI to resolve.
963      * @return The FileName of the file.
964      * @throws FileSystemException if an error occurs.
965      */
966     @Override
967     public FileName resolveURI(final String uri) throws FileSystemException {
968         UriParser.checkUriEncoding(uri);
969 
970         if (uri == null) {
971             throw new IllegalArgumentException();
972         }
973 
974         // Extract the scheme
975         final String scheme = UriParser.extractScheme(getSchemes(), uri);
976         if (scheme != null) {
977             // An absolute URI - locate the provider
978             final FileProvider provider = providers.get(scheme);
979             if (provider != null) {
980                 return provider.parseUri(null, uri);
981             }
982 
983             // Otherwise, assume a local file
984         }
985 
986         // Handle absolute file names
987         if (localFileProvider != null && localFileProvider.isAbsoluteLocalName(uri)) {
988             return localFileProvider.parseUri(null, uri);
989         }
990 
991         if (scheme != null) {
992             // An unknown scheme - hand it to the default provider
993             FileSystemException.requireNonNull(defaultProvider, "vfs.impl/unknown-scheme.error", scheme, uri);
994             return defaultProvider.parseUri(null, uri);
995         }
996 
997         // Assume a relative name - use the supplied base file
998         FileSystemException.requireNonNull(baseFile, "vfs.impl/find-rel-file.error", uri);
999 
1000         return resolveName(baseFile.getName(), uri, NameScope.FILE_SYSTEM);
1001     }
1002 
1003     /**
1004      * Sets the base file to use when resolving relative URI.
1005      *
1006      * @param baseFile The new base FileObject.
1007      * @throws FileSystemException if an error occurs.
1008      */
1009     public void setBaseFile(final File baseFile) throws FileSystemException {
1010         this.baseFile = getLocalFileProvider().findLocalFile(baseFile);
1011     }
1012 
1013     /**
1014      * Sets the base file to use when resolving relative URI.
1015      *
1016      * @param baseFile The new base FileObject.
1017      */
1018     public void setBaseFile(final FileObject baseFile) {
1019         this.baseFile = baseFile;
1020     }
1021 
1022     /**
1023      * Set the cache strategy to use when dealing with file object data.
1024      * <p>
1025      * Can only be set before the FileSystemManager is initialized.
1026      * </p>
1027      * <p>
1028      * The default is {@link CacheStrategy#ON_RESOLVE}.
1029      * </p>
1030      *
1031      * @param fileCacheStrategy The CacheStrategy to use.
1032      * @throws FileSystemException if this is not possible. e.g. it is already set.
1033      */
1034     public void setCacheStrategy(final CacheStrategy fileCacheStrategy) throws FileSystemException {
1035         if (init) {
1036             throw new FileSystemException("vfs.impl/already-inited.error");
1037         }
1038 
1039         this.fileCacheStrategy = fileCacheStrategy;
1040     }
1041 
1042     /**
1043      * Sets the default provider. This is the provider that will handle URI with unknown schemes. The manager takes care
1044      * of all lifecycle management.
1045      *
1046      * @param provider The FileProvider.
1047      * @throws FileSystemException if an error occurs setting the provider.
1048      */
1049     public void setDefaultProvider(final FileProvider provider) throws FileSystemException {
1050         setupComponent(provider);
1051         defaultProvider = provider;
1052     }
1053 
1054     /**
1055      * set the fileContentInfoFactory used to determine the infos of a file content.
1056      * <p>
1057      * Can only be set before the FileSystemManager is initialized.
1058      * </p>
1059      *
1060      * @param fileContentInfoFactory The FileContentInfoFactory.
1061      * @throws FileSystemException if an error occurs setting the FileContentInfoFactory.
1062      */
1063     public void setFileContentInfoFactory(final FileContentInfoFactory fileContentInfoFactory)
1064             throws FileSystemException {
1065         if (init) {
1066             throw new FileSystemException("vfs.impl/already-inited.error");
1067         }
1068 
1069         this.fileContentInfoFactory = fileContentInfoFactory;
1070     }
1071 
1072     /**
1073      * Set a fileObject decorator to be used for ALL returned file objects.
1074      * <p>
1075      * Can only be set before the FileSystemManager is initialized.
1076      * </p>
1077      *
1078      * @param fileObjectDecorator must be inherted from {@link DecoratedFileObject} a has to provide a constructor with
1079      *            a single {@link FileObject} as argument
1080      * @throws FileSystemException if an error occurs setting the decorator.
1081      */
1082     public void setFileObjectDecorator(final Class<?> fileObjectDecorator) throws FileSystemException {
1083         if (init) {
1084             throw new FileSystemException("vfs.impl/already-inited.error");
1085         }
1086         if (!DecoratedFileObject.class.isAssignableFrom(fileObjectDecorator)) {
1087             throw new FileSystemException("vfs.impl/invalid-decorator.error", fileObjectDecorator.getName());
1088         }
1089 
1090         try {
1091             fileObjectDecoratorConst = fileObjectDecorator.getConstructor(FileObject.class);
1092         } catch (final NoSuchMethodException e) {
1093             throw new FileSystemException("vfs.impl/invalid-decorator.error", fileObjectDecorator.getName(), e);
1094         }
1095 
1096         this.fileObjectDecorator = fileObjectDecorator;
1097     }
1098 
1099     /**
1100      * Sets the filesCache implementation used to cache files.
1101      * <p>
1102      * Can only be set before the FileSystemManager is initialized.
1103      * </p>
1104      * <p>
1105      * The manager takes care of the lifecycle. If none is set, a default is picked in {@link #init()}.
1106      * </p>
1107      *
1108      * @param filesCache The FilesCache.
1109      * @throws FileSystemException if an error occurs setting the cache..
1110      */
1111     public void setFilesCache(final FilesCache filesCache) throws FileSystemException {
1112         if (init) {
1113             throw new FileSystemException("vfs.impl/already-inited.error");
1114         }
1115         this.filesCache = filesCache;
1116     }
1117 
1118     // -- OPERATIONS --
1119 
1120     /**
1121      * Sets the logger to use.
1122      * <p>
1123      * This overwrites the default logger for this manager and is not reset in {@link #close()}.
1124      * </p>
1125      *
1126      * @param log The Logger to use.
1127      */
1128     @Override
1129     public void setLogger(final Log log) {
1130         this.log = log;
1131     }
1132 
1133     /**
1134      * Sets the file replicator to use.
1135      * <p>
1136      * The manager takes care of all lifecycle management.
1137      * </p>
1138      *
1139      * @param replicator The FileReplicator.
1140      * @throws FileSystemException if an error occurs setting the replicator.
1141      */
1142     public void setReplicator(final FileReplicator replicator) throws FileSystemException {
1143         setupComponent(replicator);
1144         fileReplicator = replicator;
1145     }
1146 
1147     /**
1148      * Sets the temporary file store to use.
1149      * <p>
1150      * The manager takes care of all lifecycle management.
1151      * </p>
1152      *
1153      * @param tempFileStore The temporary FileStore.
1154      * @throws FileSystemException if an error occurs adding the file store.
1155      */
1156     public void setTemporaryFileStore(final TemporaryFileStore tempFileStore) throws FileSystemException {
1157         setupComponent(tempFileStore);
1158         this.tempFileStore = tempFileStore;
1159     }
1160 
1161     /**
1162      * Initializes a component, if it has not already been initialized.
1163      *
1164      * @param component The component to setup.
1165      * @throws FileSystemException if an error occurs.
1166      */
1167     private void setupComponent(final Object component) throws FileSystemException {
1168         if (!components.contains(component)) {
1169             if (component instanceof VfsComponent) {
1170                 final VfsComponent../org/apache/commons/vfs2/provider/VfsComponent.html#VfsComponent">VfsComponent vfsComponent = (VfsComponent) component;
1171                 vfsComponent.setLogger(getLogger());
1172                 vfsComponent.setContext(context);
1173                 vfsComponent.init();
1174             }
1175             components.add(component);
1176         }
1177     }
1178 
1179     /**
1180      * Converts a local file into a {@link FileObject}.
1181      *
1182      * @param file The input File.
1183      * @return the create FileObject
1184      * @throws FileSystemException if an error occurs creating the file.
1185      */
1186     @Override
1187     public FileObject toFileObject(final File file) throws FileSystemException {
1188         return getLocalFileProvider().findLocalFile(file);
1189     }
1190 
1191 }