Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.schlichtherle.truezip.fs.FsSyncController Maven / Gradle / Ivy
/*
* Copyright (C) 2005-2015 Schlichtherle IT Services.
* All rights reserved. Use is subject to license terms.
*/
package de.schlichtherle.truezip.fs;
import de.schlichtherle.truezip.entry.Entry;
import de.schlichtherle.truezip.entry.Entry.Access;
import de.schlichtherle.truezip.entry.Entry.Type;
import static de.schlichtherle.truezip.fs.FsSyncOption.FORCE_CLOSE_INPUT;
import static de.schlichtherle.truezip.fs.FsSyncOption.FORCE_CLOSE_OUTPUT;
import static de.schlichtherle.truezip.fs.FsSyncOption.WAIT_CLOSE_INPUT;
import static de.schlichtherle.truezip.fs.FsSyncOption.WAIT_CLOSE_OUTPUT;
import static de.schlichtherle.truezip.fs.FsSyncOptions.RESET;
import static de.schlichtherle.truezip.fs.FsSyncOptions.SYNC;
import de.schlichtherle.truezip.io.DecoratingInputStream;
import de.schlichtherle.truezip.io.DecoratingOutputStream;
import de.schlichtherle.truezip.io.DecoratingSeekableByteChannel;
import de.schlichtherle.truezip.rof.DecoratingReadOnlyFile;
import de.schlichtherle.truezip.rof.ReadOnlyFile;
import de.schlichtherle.truezip.socket.DecoratingInputSocket;
import de.schlichtherle.truezip.socket.DecoratingOutputSocket;
import de.schlichtherle.truezip.socket.InputSocket;
import de.schlichtherle.truezip.socket.OutputSocket;
import de.schlichtherle.truezip.util.BitField;
import de.schlichtherle.truezip.util.JSE7;
import edu.umd.cs.findbugs.annotations.CreatesObligation;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.WillCloseWhenClosed;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
/**
* Performs a {@link FsController#sync(BitField) sync} operation on the
* file system if and only if any decorated file system controller throws an
* {@link FsNeedsSyncException}.
*
* @see FsNeedsSyncException
* @since TrueZIP 7.3
* @author Christian Schlichtherle
*/
@ThreadSafe
final class FsSyncController
extends FsLockModelDecoratingController> {
private static final SocketFactory SOCKET_FACTORY = JSE7.AVAILABLE
? SocketFactory.NIO2
: SocketFactory.OIO;
private static final BitField NOT_WAIT_CLOSE_IO
= BitField.of(WAIT_CLOSE_INPUT, WAIT_CLOSE_OUTPUT).not();
/**
* Constructs a new file system sync controller.
*
* @param controller the decorated file system controller.
*/
FsSyncController(FsController extends FsLockModel> controller) {
super(controller);
}
void sync(final FsNeedsSyncException trigger)
throws FsSyncException {
checkWriteLockedByCurrentThread();
try {
sync(SYNC);
} catch (final FsSyncException ex) {
if (JSE7.AVAILABLE) ex.addSuppressed(trigger);
throw ex;
}
}
@Override
public boolean isReadOnly() throws IOException {
while (true) {
try {
return delegate.isReadOnly();
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public FsEntry getEntry(final FsEntryName name)
throws IOException {
while (true) {
try {
return delegate.getEntry(name);
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public boolean isReadable(final FsEntryName name) throws IOException {
while (true) {
try {
return delegate.isReadable(name);
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public boolean isWritable(final FsEntryName name) throws IOException {
while (true) {
try {
return delegate.isWritable(name);
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public boolean isExecutable(final FsEntryName name) throws IOException {
while (true) {
try {
return delegate.isExecutable(name);
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public void setReadOnly(final FsEntryName name) throws IOException {
while (true) {
try {
delegate.setReadOnly(name);
return;
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public boolean setTime(
final FsEntryName name,
final Map times,
final BitField options)
throws IOException {
while (true) {
try {
return delegate.setTime(name, times, options);
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public boolean setTime(
final FsEntryName name,
final BitField types,
final long value,
final BitField options)
throws IOException {
while (true) {
try {
return delegate.setTime(name, types, value, options);
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public InputSocket> getInputSocket( FsEntryName name,
BitField options) {
return SOCKET_FACTORY.newInputSocket(this, name, options);
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE")
public OutputSocket> getOutputSocket( FsEntryName name,
BitField options,
@CheckForNull Entry template) {
return SOCKET_FACTORY.newOutputSocket(this, name, options, template);
}
@Override
public void mknod(
final FsEntryName name,
final Type type,
final BitField options,
final @CheckForNull Entry template)
throws IOException {
while (true) {
try {
delegate.mknod(name, type, options, template);
return;
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public void unlink(
final FsEntryName name,
final BitField options)
throws IOException {
while (true) {
try {
// HC SVNT DRACONES!
delegate.unlink(name, options);
if (name.isRoot()) {
// Make the file system controller chain eligible for GC.
delegate.sync(RESET);
}
return;
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public void sync(final BitField options)
throws FsSyncException {
assert isWriteLockedByCurrentThread();
assert !isReadLockedByCurrentThread();
// HC SVNT DRACONES!
final BitField modified = modify(options);
while (true) {
try {
delegate.sync(modified);
return;
} catch (final FsSyncException ex) {
if (ex.getCause() instanceof FsResourceOpenException
&& modified != options) {
assert(!(ex instanceof FsSyncWarningException));
// Swallow ex.
throw FsNeedsLockRetryException.get();
} else {
throw ex;
}
} catch (FsNeedsSyncException yeahIKnow_IWasActuallyDoingThat) {
// This exception was thrown by the resource controller in
// order to indicate that the state of the virtual file
// system may have completely changed as a side effect of
// temporarily releasing its write lock.
// The sync operation needs to get repeated.
}
}
}
private static boolean forceCloseIo(final BitField options) {
return options.get(FORCE_CLOSE_INPUT)
|| options.get(FORCE_CLOSE_OUTPUT);
}
/**
* Modify the sync options so that no dead lock can appear due to waiting
* for I/O resources in a recursive file system operation.
*
* @param options the sync options
* @return the potentially modified sync options.
*/
static BitField modify(final BitField options) {
final boolean isRecursive = 1 < FsLockController.getLockCount();
final BitField result = isRecursive
? options.and(NOT_WAIT_CLOSE_IO)
: options;
assert result == options == result.equals(options) : "Broken contract in BitField.and()!";
assert result == options || isRecursive;
return result;
}
void close(final Closeable closeable) throws IOException {
while (true) {
try {
closeable.close();
return;
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Immutable
private enum SocketFactory {
NIO2() {
@Override
InputSocket> newInputSocket(
FsSyncController controller,
FsEntryName name,
BitField options) {
return controller.new Nio2Input(name, options);
}
@Override
OutputSocket> newOutputSocket(
FsSyncController controller,
FsEntryName name,
BitField options,
@CheckForNull Entry template) {
return controller.new Nio2Output(name, options, template);
}
},
OIO() {
@Override
InputSocket> newInputSocket(
FsSyncController controller,
FsEntryName name,
BitField options) {
return controller.new Input(name, options);
}
@Override
OutputSocket> newOutputSocket(
FsSyncController controller,
FsEntryName name,
BitField options,
@CheckForNull Entry template) {
return controller.new Output(name, options, template);
}
};
abstract InputSocket> newInputSocket(
FsSyncController controller,
FsEntryName name,
BitField options);
abstract OutputSocket> newOutputSocket(
FsSyncController controller,
FsEntryName name,
BitField options,
@CheckForNull Entry template);
} // SocketFactory
@Immutable
private final class Nio2Input extends Input {
Nio2Input( final FsEntryName name,
final BitField options) {
super(name, options);
}
@Override
public SeekableByteChannel newSeekableByteChannel() throws IOException {
while (true) {
try {
return new SyncSeekableByteChannel(
getBoundSocket().newSeekableByteChannel());
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
} // Nio2Input
@Immutable
private class Input extends DecoratingInputSocket {
Input( final FsEntryName name,
final BitField options) {
super(FsSyncController.this.delegate
.getInputSocket(name, options));
}
@Override
public Entry getLocalTarget() throws IOException {
while (true) {
try {
return getBoundSocket().getLocalTarget();
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public ReadOnlyFile newReadOnlyFile() throws IOException {
while (true) {
try {
return new SyncReadOnlyFile(
getBoundSocket().newReadOnlyFile());
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public InputStream newInputStream() throws IOException {
while (true) {
try {
return new SyncInputStream(
getBoundSocket().newInputStream());
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
} // Input
@Immutable
private final class Nio2Output extends Output {
Nio2Output( final FsEntryName name,
final BitField options,
final @CheckForNull Entry template) {
super(name, options, template);
}
@Override
public SeekableByteChannel newSeekableByteChannel() throws IOException {
while (true) {
try {
return new SyncSeekableByteChannel(
getBoundSocket().newSeekableByteChannel());
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
} // Nio2Output
@Immutable
private class Output extends DecoratingOutputSocket {
Output( final FsEntryName name,
final BitField options,
final @CheckForNull Entry template) {
super(FsSyncController.this.delegate
.getOutputSocket(name, options, template));
}
@Override
public Entry getLocalTarget() throws IOException {
while (true) {
try {
return getBoundSocket().getLocalTarget();
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
@Override
public OutputStream newOutputStream() throws IOException {
while (true) {
try {
return new SyncOutputStream(
getBoundSocket().newOutputStream());
} catch (FsNeedsSyncException ex) {
sync(ex);
}
}
}
} // Output
private final class SyncReadOnlyFile
extends DecoratingReadOnlyFile {
@CreatesObligation
@edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
SyncReadOnlyFile(@WillCloseWhenClosed ReadOnlyFile rof) {
super(rof);
}
@Override
public void close() throws IOException {
FsSyncController.this.close(delegate);
}
} // SyncReadOnlyFile
private final class SyncSeekableByteChannel
extends DecoratingSeekableByteChannel {
@CreatesObligation
@edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
SyncSeekableByteChannel(@WillCloseWhenClosed SeekableByteChannel sbc) {
super(sbc);
}
@Override
public void close() throws IOException {
FsSyncController.this.close(delegate);
}
} // SyncSeekableByteChannel
private final class SyncInputStream
extends DecoratingInputStream {
@CreatesObligation
@edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
SyncInputStream(@WillCloseWhenClosed InputStream in) {
super(in);
}
@Override
public void close() throws IOException {
FsSyncController.this.close(delegate);
}
} // SyncInputStream
private final class SyncOutputStream
extends DecoratingOutputStream {
@CreatesObligation
@edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
SyncOutputStream(@WillCloseWhenClosed OutputStream out) {
super(out);
}
@Override
public void close() throws IOException {
FsSyncController.this.close(delegate);
}
} // SyncOutputStream
}