/* * 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 java.util.Map; import java.util.Set; /** * Used by MockDirectoryWrapper to create an input stream that * keeps track of when it's been closed. */ public class MockIndexInputWrapper extends IndexInput { private MockDirectoryWrapper dir; final String name; private IndexInput delegate; private volatile boolean closed; // Which MockIndexInputWrapper we were cloned from, or null if we are not a clone: private final MockIndexInputWrapper parent; /** Sole constructor */ public MockIndexInputWrapper(MockDirectoryWrapper dir, String name, IndexInput delegate, MockIndexInputWrapper parent) { super("MockIndexInputWrapper(name=" + name + " delegate=" + delegate + ")"); // If we are a clone then our parent better not be a clone! assert parent == null || parent.parent == null; this.parent = parent; this.name = name; this.dir = dir; this.delegate = delegate; } @Override public void close() throws IOException { if (closed) { delegate.close(); // don't mask double-close bugs return; } closed = true; try (Closeable delegate = this.delegate) { // Pending resolution on LUCENE-686 we may want to // remove the conditional check so we also track that // all clones get closed: assert delegate != null; if (parent == null) { dir.removeIndexInput(this, name); } dir.maybeThrowDeterministicException(); } } private void ensureOpen() { // TODO: not great this is a volatile read (closed) ... we should deploy heavy JVM voodoo like SwitchPoint to avoid this if (closed) { throw new RuntimeException("Abusing closed IndexInput!"); } if (parent != null && parent.closed) { throw new RuntimeException("Abusing clone of a closed IndexInput!"); } } @Override public MockIndexInputWrapper clone() { ensureOpen(); if (dir.verboseClone) { new Exception("clone: " + this).printStackTrace(System.out); } dir.inputCloneCount.incrementAndGet(); IndexInput iiclone = delegate.clone(); MockIndexInputWrapper clone = new MockIndexInputWrapper(dir, name, iiclone, parent != null ? parent : this); // Pending resolution on LUCENE-686 we may want to // uncomment this code so that we also track that all // clones get closed: /* synchronized(dir.openFiles) { if (dir.openFiles.containsKey(name)) { Integer v = (Integer) dir.openFiles.get(name); v = Integer.valueOf(v.intValue()+1); dir.openFiles.put(name, v); } else { throw new RuntimeException("BUG: cloned file was not open?"); } } */ return clone; } @Override public IndexInput slice(String sliceDescription, long offset, long length) throws IOException { ensureOpen(); if (dir.verboseClone) { new Exception("slice: " + this).printStackTrace(System.out); } dir.inputCloneCount.incrementAndGet(); IndexInput slice = delegate.slice(sliceDescription, offset, length); MockIndexInputWrapper clone = new MockIndexInputWrapper(dir, sliceDescription, slice, parent != null ? parent : this); return clone; } @Override public long getFilePointer() { ensureOpen(); return delegate.getFilePointer(); } @Override public void seek(long pos) throws IOException { ensureOpen(); delegate.seek(pos); } @Override public long length() { ensureOpen(); return delegate.length(); } @Override public byte readByte() throws IOException { ensureOpen(); return delegate.readByte(); } @Override public void readBytes(byte[] b, int offset, int len) throws IOException { ensureOpen(); delegate.readBytes(b, offset, len); } @Override public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException { ensureOpen(); delegate.readBytes(b, offset, len, useBuffer); } @Override public short readShort() throws IOException { ensureOpen(); return delegate.readShort(); } @Override public int readInt() throws IOException { ensureOpen(); return delegate.readInt(); } @Override public long readLong() throws IOException { ensureOpen(); return delegate.readLong(); } @Override public String readString() throws IOException { ensureOpen(); return delegate.readString(); } @Override public int readVInt() throws IOException { ensureOpen(); return delegate.readVInt(); } @Override public long readVLong() throws IOException { ensureOpen(); return delegate.readVLong(); } @Override public int readZInt() throws IOException { ensureOpen(); return delegate.readZInt(); } @Override public long readZLong() throws IOException { ensureOpen(); return delegate.readZLong(); } @Override public void skipBytes(long numBytes) throws IOException { ensureOpen(); super.skipBytes(numBytes); } @Override public Map<String,String> readMapOfStrings() throws IOException { ensureOpen(); return delegate.readMapOfStrings(); } @Override public Set<String> readSetOfStrings() throws IOException { ensureOpen(); return delegate.readSetOfStrings(); } @Override public String toString() { return "MockIndexInputWrapper(" + delegate + ")"; } }