org.apache.hadoop.io.compress.CodecPool.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.io.compress.CodecPool.java

Source

/**
 * 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.io.compress;

import java.util.HashSet;
import java.util.HashMap;
import java.util.Set;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.util.ReflectionUtils;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A global compressor/decompressor pool used to save and reuse 
 * (possibly native) compression/decompression codecs.
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public class CodecPool {
    private static final Logger LOG = LoggerFactory.getLogger(CodecPool.class);

    /**
     * A global compressor pool used to save the expensive 
     * construction/destruction of (possibly native) decompression codecs.
     */
    private static final Map<Class<Compressor>, Set<Compressor>> compressorPool = new HashMap<Class<Compressor>, Set<Compressor>>();

    /**
     * A global decompressor pool used to save the expensive 
     * construction/destruction of (possibly native) decompression codecs.
     */
    private static final Map<Class<Decompressor>, Set<Decompressor>> decompressorPool = new HashMap<Class<Decompressor>, Set<Decompressor>>();

    private static <T> LoadingCache<Class<T>, AtomicInteger> createCache(Class<T> klass) {
        return CacheBuilder.newBuilder().build(new CacheLoader<Class<T>, AtomicInteger>() {
            @Override
            public AtomicInteger load(Class<T> key) throws Exception {
                return new AtomicInteger();
            }
        });
    }

    /**
     * Map to track the number of leased compressors
     */
    private static final LoadingCache<Class<Compressor>, AtomicInteger> compressorCounts = createCache(
            Compressor.class);

    /**
    * Map to tracks the number of leased decompressors
    */
    private static final LoadingCache<Class<Decompressor>, AtomicInteger> decompressorCounts = createCache(
            Decompressor.class);

    private static <T> T borrow(Map<Class<T>, Set<T>> pool, Class<? extends T> codecClass) {
        T codec = null;

        // Check if an appropriate codec is available
        Set<T> codecSet;
        synchronized (pool) {
            codecSet = pool.get(codecClass);
        }

        if (codecSet != null) {
            synchronized (codecSet) {
                if (!codecSet.isEmpty()) {
                    codec = codecSet.iterator().next();
                    codecSet.remove(codec);
                }
            }
        }

        return codec;
    }

    private static <T> boolean payback(Map<Class<T>, Set<T>> pool, T codec) {
        if (codec != null) {
            Class<T> codecClass = ReflectionUtils.getClass(codec);
            Set<T> codecSet;
            synchronized (pool) {
                codecSet = pool.get(codecClass);
                if (codecSet == null) {
                    codecSet = new HashSet<T>();
                    pool.put(codecClass, codecSet);
                }
            }

            synchronized (codecSet) {
                return codecSet.add(codec);
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    private static <T> int getLeaseCount(LoadingCache<Class<T>, AtomicInteger> usageCounts,
            Class<? extends T> codecClass) {
        return usageCounts.getUnchecked((Class<T>) codecClass).get();
    }

    private static <T> void updateLeaseCount(LoadingCache<Class<T>, AtomicInteger> usageCounts, T codec,
            int delta) {
        if (codec != null) {
            Class<T> codecClass = ReflectionUtils.getClass(codec);
            usageCounts.getUnchecked(codecClass).addAndGet(delta);
        }
    }

    /**
     * Get a {@link Compressor} for the given {@link CompressionCodec} from the 
     * pool or a new one.
     *
     * @param codec the <code>CompressionCodec</code> for which to get the 
     *              <code>Compressor</code>
     * @param conf the <code>Configuration</code> object which contains confs for creating or reinit the compressor
     * @return <code>Compressor</code> for the given 
     *         <code>CompressionCodec</code> from the pool or a new one
     */
    public static Compressor getCompressor(CompressionCodec codec, Configuration conf) {
        Compressor compressor = borrow(compressorPool, codec.getCompressorType());
        if (compressor == null) {
            compressor = codec.createCompressor();
            LOG.info("Got brand-new compressor [" + codec.getDefaultExtension() + "]");
        } else {
            compressor.reinit(conf);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Got recycled compressor");
            }
        }
        if (compressor != null && !compressor.getClass().isAnnotationPresent(DoNotPool.class)) {
            updateLeaseCount(compressorCounts, compressor, 1);
        }
        return compressor;
    }

    public static Compressor getCompressor(CompressionCodec codec) {
        return getCompressor(codec, null);
    }

    /**
     * Get a {@link Decompressor} for the given {@link CompressionCodec} from the
     * pool or a new one.
     *  
     * @param codec the <code>CompressionCodec</code> for which to get the 
     *              <code>Decompressor</code>
     * @return <code>Decompressor</code> for the given 
     *         <code>CompressionCodec</code> the pool or a new one
     */
    public static Decompressor getDecompressor(CompressionCodec codec) {
        Decompressor decompressor = borrow(decompressorPool, codec.getDecompressorType());
        if (decompressor == null) {
            decompressor = codec.createDecompressor();
            LOG.info("Got brand-new decompressor [" + codec.getDefaultExtension() + "]");
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Got recycled decompressor");
            }
        }
        if (decompressor != null && !decompressor.getClass().isAnnotationPresent(DoNotPool.class)) {
            updateLeaseCount(decompressorCounts, decompressor, 1);
        }
        return decompressor;
    }

    /**
     * Return the {@link Compressor} to the pool.
     * 
     * @param compressor the <code>Compressor</code> to be returned to the pool
     */
    public static void returnCompressor(Compressor compressor) {
        if (compressor == null) {
            return;
        }
        // if the compressor can't be reused, don't pool it.
        if (compressor.getClass().isAnnotationPresent(DoNotPool.class)) {
            return;
        }
        compressor.reset();
        if (payback(compressorPool, compressor)) {
            updateLeaseCount(compressorCounts, compressor, -1);
        }
    }

    /**
     * Return the {@link Decompressor} to the pool.
     * 
     * @param decompressor the <code>Decompressor</code> to be returned to the 
     *                     pool
     */
    public static void returnDecompressor(Decompressor decompressor) {
        if (decompressor == null) {
            return;
        }
        // if the decompressor can't be reused, don't pool it.
        if (decompressor.getClass().isAnnotationPresent(DoNotPool.class)) {
            return;
        }
        decompressor.reset();
        if (payback(decompressorPool, decompressor)) {
            updateLeaseCount(decompressorCounts, decompressor, -1);
        }
    }

    /**
     * Return the number of leased {@link Compressor}s for this
     * {@link CompressionCodec}
     */
    public static int getLeasedCompressorsCount(CompressionCodec codec) {
        return (codec == null) ? 0 : getLeaseCount(compressorCounts, codec.getCompressorType());
    }

    /**
     * Return the number of leased {@link Decompressor}s for this
     * {@link CompressionCodec}
     */
    public static int getLeasedDecompressorsCount(CompressionCodec codec) {
        return (codec == null) ? 0 : getLeaseCount(decompressorCounts, codec.getDecompressorType());
    }
}