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