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