| /* |
| * 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 + ")"; |
| } |
| } |