blob: 510e930f7a5be51ee7cb143936f16dbd457eb1bf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.lucene.store;
import java.io.Closeable;
import java.io.IOException;
import org.apache.lucene.util.LuceneTestCase;
/**
* Used by MockRAMDirectory to create an output stream that
* will throw an IOException on fake disk full, track max
* disk space actually used, and maybe throw random
* IOExceptions.
*/
public class MockIndexOutputWrapper extends IndexOutput {
private MockDirectoryWrapper dir;
private final IndexOutput delegate;
private boolean first=true;
final String name;
byte[] singleByte = new byte[1];
/** Construct an empty output buffer. */
public MockIndexOutputWrapper(MockDirectoryWrapper dir, IndexOutput delegate, String name) {
super("MockIndexOutputWrapper(" + delegate + ")", delegate.getName());
this.dir = dir;
this.name = name;
this.delegate = delegate;
}
private void checkCrashed() throws IOException {
// If MockRAMDir crashed since we were opened, then don't write anything
if (dir.crashed) {
throw new IOException("MockRAMDirectory was crashed; cannot write to " + name);
}
}
private void checkDiskFull(byte[] b, int offset, DataInput in, long len) throws IOException {
long freeSpace = dir.maxSize == 0 ? 0 : dir.maxSize - dir.sizeInBytes();
long realUsage = 0;
// Enforce disk full:
if (dir.maxSize != 0 && freeSpace <= len) {
// Compute the real disk free. This will greatly slow
// down our test but makes it more accurate:
realUsage = dir.getRecomputedActualSizeInBytes();
freeSpace = dir.maxSize - realUsage;
}
if (dir.maxSize != 0 && freeSpace <= len) {
if (freeSpace > 0) {
realUsage += freeSpace;
if (b != null) {
delegate.writeBytes(b, offset, (int) freeSpace);
} else {
delegate.copyBytes(in, (int) freeSpace);
}
}
if (realUsage > dir.maxUsedSize) {
dir.maxUsedSize = realUsage;
}
String message = "fake disk full at " + dir.getRecomputedActualSizeInBytes() + " bytes when writing " + name + " (file length=" + delegate.getFilePointer();
if (freeSpace > 0) {
message += "; wrote " + freeSpace + " of " + len + " bytes";
}
message += ")";
if (LuceneTestCase.VERBOSE) {
System.out.println(Thread.currentThread().getName() + ": MDW: now throw fake disk full");
new Throwable().printStackTrace(System.out);
}
throw new IOException(message);
}
}
private boolean closed;
@Override
public void close() throws IOException {
if (closed) {
delegate.close(); // don't mask double-close bugs
return;
}
closed = true;
try (Closeable delegate = this.delegate) {
assert delegate != null;
dir.maybeThrowDeterministicException();
} finally {
dir.removeIndexOutput(this, name);
if (dir.trackDiskUsage) {
// Now compute actual disk usage & track the maxUsedSize
// in the MockDirectoryWrapper:
long size = dir.getRecomputedActualSizeInBytes();
if (size > dir.maxUsedSize) {
dir.maxUsedSize = size;
}
}
}
}
private void ensureOpen() {
if (closed) {
throw new AlreadyClosedException("Already closed: " + this);
}
}
@Override
public void writeByte(byte b) throws IOException {
singleByte[0] = b;
writeBytes(singleByte, 0, 1);
}
@Override
public void writeBytes(byte[] b, int offset, int len) throws IOException {
ensureOpen();
checkCrashed();
checkDiskFull(b, offset, null, len);
if (dir.randomState.nextInt(200) == 0) {
final int half = len/2;
delegate.writeBytes(b, offset, half);
Thread.yield();
delegate.writeBytes(b, offset+half, len-half);
} else {
delegate.writeBytes(b, offset, len);
}
dir.maybeThrowDeterministicException();
if (first) {
// Maybe throw random exception; only do this on first
// write to a new file:
first = false;
dir.maybeThrowIOException(name);
}
}
@Override
public long getFilePointer() {
return delegate.getFilePointer();
}
@Override
public void copyBytes(DataInput input, long numBytes) throws IOException {
ensureOpen();
checkCrashed();
checkDiskFull(null, 0, input, numBytes);
delegate.copyBytes(input, numBytes);
dir.maybeThrowDeterministicException();
}
@Override
public long getChecksum() throws IOException {
return delegate.getChecksum();
}
@Override
public String toString() {
return "MockIndexOutputWrapper(" + delegate + ")";
}
}