All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.io.ArchiveFileSystemController Maven / Gradle / Ivy

/*
 * Copyright (C) 2006-2010 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.schlichtherle.io;

import de.schlichtherle.io.archive.spi.*;

import java.io.*;

/**
 * This archive controller implements the automounting functionality.
 * It is up to the sub class to implement the actual mounting/unmounting
 * strategy.
 *
 * @author Christian Schlichtherle
 * @version $Id: ArchiveFileSystemController.java,v 1.4 2010/08/20 13:09:42 christian_schlichtherle Exp $
 * @since TrueZIP 6.5
 */
abstract class ArchiveFileSystemController extends ArchiveController {

    /** The mount state of the archive file system. */
    private AutoMounter autoMounter = new ResetFileSystem();

    /**
     * Creates a new instance of ArchiveFileSystemController
     */
    ArchiveFileSystemController(
            java.io.File target,
            ArchiveController enclController,
            String enclEntryName,
            ArchiveDriver driver) {
        super(target, enclController, enclEntryName, driver);
    }

    final boolean isTouched() {
        ArchiveFileSystem fileSystem = getFileSystem();
        return fileSystem != null && fileSystem.isTouched();
    }

    /**
     * Called by this controller's {@link ArchiveFileSystem} to notify it
     * that the file system has been touched.
     * A file system is touched if an operation has been performed on it
     * which modifies it.
     * 

* Warning: The write lock of this controller must * be acquired while this method is called! */ void touch() throws IOException { assert writeLock().isLocked(); setScheduled(true); } final ArchiveFileSystem autoMount(final boolean create) throws IOException { assert readLock().isLocked() || writeLock().isLocked(); return autoMounter.autoMount(create); } final ArchiveFileSystem getFileSystem() { return autoMounter.getFileSystem(); } final void setFileSystem(ArchiveFileSystem fileSystem) { autoMounter.setFileSystem(fileSystem); } /** * Represents the mount state of the archive file system. * This is an abstract class: The state is implemented in the sub classes. */ private static abstract class AutoMounter { abstract ArchiveFileSystem autoMount(boolean create) throws IOException; ArchiveFileSystem getFileSystem() { return null; } abstract void setFileSystem(ArchiveFileSystem fileSystem); } // class AutoMounter private class ResetFileSystem extends AutoMounter { ArchiveFileSystem autoMount(final boolean create) throws IOException { try { class Mounter implements IORunnable { public void run() throws IOException { // Check state again: Another thread may have changed // it while we released all read locks in order to // acquire the write lock! if (autoMounter == ResetFileSystem.this) { mount(create); assert autoMounter instanceof MountedFileSystem; } else { assert autoMounter != null; assert !(autoMounter instanceof ResetFileSystem); } } } // class Mounter runWriteLocked(new Mounter()); } catch (FalsePositiveException ex) { // Catch and cache exceptions for uncacheable false positives. // The state is reset when File.delete() is called on the false // positive archive file or File.update() or File.umount(). // This is an important optimization: When hitting a false // positive archive file, a client application might perform // a lot of tests on it (isDirectory(), isFile(), exists(), // length(), etc). If the exception were not cached, each call // would run the file system initialization again, only to // result in another instance of the same exception type again. // Note that it is important to cache the exceptions for // cacheable false positives only: Otherwise, side effects // of the archive driver may not be accounted for. if (ex.isCacheable()) autoMounter = new FalsePositiveFileSystem(ex); throw ex; } assert autoMounter != this; // DON'T just call autoMounter.getFileSystem()! // This would return null if autoMounter is an instance of // FalsePositiveFileSystem. return autoMounter.autoMount(create); } void setFileSystem(ArchiveFileSystem fileSystem) { // Passing in null may happen by reset(). if (fileSystem != null) autoMounter = new MountedFileSystem(fileSystem); } } // class ResetFileSystem private class MountedFileSystem extends AutoMounter { private final ArchiveFileSystem fileSystem; private MountedFileSystem(final ArchiveFileSystem fileSystem) { assert fileSystem != null : "It's illegal to use this state with null as the file system!"; this.fileSystem = fileSystem; } ArchiveFileSystem autoMount(boolean create) throws IOException { return fileSystem; } ArchiveFileSystem getFileSystem() { return fileSystem; } void setFileSystem(ArchiveFileSystem fileSystem) { assert fileSystem == null : "It's illegal to assign a file system to an archive controller which already has its file system mounted!"; autoMounter = new ResetFileSystem(); } } // class MountedFileSystem private class FalsePositiveFileSystem extends AutoMounter { private final FalsePositiveException exception; private FalsePositiveFileSystem(final FalsePositiveException exception) { assert exception != null : "It's illegal to use this state with null as the IOException!"; this.exception = exception; } ArchiveFileSystem autoMount(boolean create) throws IOException { throw exception; } void setFileSystem(ArchiveFileSystem fileSystem) { assert fileSystem == null : "It's illegal to assign a file system to an archive controller for a false positive archive file!"; autoMounter = new ResetFileSystem(); } } // class FalsePositiveFileSystem /** * Mounts the virtual file system from the target file. * This method is called while the write lock to mount the file system * for this controller is acquired. *

* Upon normal termination, this method is expected to have called * {@link setFileSystem} to assign the fully initialized file system * to this controller. * Other than this, the method must not have any side effects on the * state of this class or its super class. * It may, however, have side effects on the state of the sub class. * * @param create If the archive file does not exist and this is * {@code true}, a new file system with only a virtual root * directory is created with its last modification time set to the * system's current time. * @throws FalsePositiveException * @throws IOException On any other I/O related issue with the target file * or the target file of any enclosing archive file's controller. */ abstract void mount(boolean create) throws IOException; void reset() throws IOException { setFileSystem(null); } final ArchiveEntry createArchiveEntry( String entryName, ArchiveEntry blueprint) throws CharConversionException { return getDriver().createArchiveEntry(this, entryName, blueprint); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy