/** * 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.hadoop.hive.conf; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * LoopingByteArrayInputStream. * * This was designed specifically to handle the problem in Hadoop's Configuration object that it * tries to read the entire contents of the same InputStream repeatedly without resetting it. * * The Configuration object does attempt to close the InputStream though, so, since close does * nothing for the ByteArrayInputStream object, override it to reset it. * * It also uses a thread local ByteArrayInputStream for method calls. This is because * Configuration's copy constructor does a shallow copy of the resources, meaning that when the * copy constructor is used to generate HiveConfs in different threads, they all share the same * LoopingByteArrayInputStream. ByteArrayInputStreams are not thread safe in such situations. */ public class LoopingByteArrayInputStream extends InputStream { private final byte[] buf; public LoopingByteArrayInputStream(byte[] buf) { this.buf = buf; } private final ThreadLocal<ByteArrayInputStream> threadLocalByteArrayInputStream = new ThreadLocal<ByteArrayInputStream>() { @Override protected ByteArrayInputStream initialValue() { return null; } }; private ByteArrayInputStream getByteArrayInputStream() { ByteArrayInputStream bais = threadLocalByteArrayInputStream.get(); if (bais == null) { bais = new ByteArrayInputStream(buf); threadLocalByteArrayInputStream.set(bais); } return bais; } @Override public synchronized int available() { return getByteArrayInputStream().available(); } @Override public void mark(int arg0) { getByteArrayInputStream().mark(arg0); } @Override public boolean markSupported() { return getByteArrayInputStream().markSupported(); } @Override public synchronized int read() { return getByteArrayInputStream().read(); } @Override public synchronized int read(byte[] arg0, int arg1, int arg2) { return getByteArrayInputStream().read(arg0, arg1, arg2); } @Override public synchronized void reset() { getByteArrayInputStream().reset(); } @Override public synchronized long skip(long arg0) { return getByteArrayInputStream().skip(arg0); } @Override public int read(byte[] arg0) throws IOException { return getByteArrayInputStream().read(arg0); } @Override public void close() throws IOException { getByteArrayInputStream().reset(); // According to the Java documentation this does nothing, but just in case getByteArrayInputStream().close(); } }