CloseableBufferFactory.java

/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2016 ForgeRock AS.
 */
package org.forgerock.http.apache.async;

import java.io.Closeable;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.forgerock.util.Factory;

/**
 * A {@link Factory} of a pool of preallocated NIO {@link Buffer} exposed as instances of {@link Closeable}.
 * The method {@link CloseableBuffer#close()} releases the instance into the pool.
 * The pool will grow in size up to the maximum concurrent threads that call an instance of the {@link Factory}.
 *
 * It can be used like this :
 * {@code
 * CloseableBufferFactory<ByteBuffer> closeableByteBufferFactory = new CloseableBufferFactory<ByteBuffer>(threadCount) {
 *     @Override
 *     protected java.nio.ByteBuffer allocate() {
 *         return ByteBuffer.allocate(8 * 1_024);
 *     }
 * };
 * try (CloseableBufferFactory.CloseableBuffer<ByteBuffer> buffer = closeableByteBufferFactory.newInstance()) {
 *     ByteBuffer byteBuffer = buffer.getBuffer();
 *     // Use the byteBuffer
 * }
 * }
 */
abstract class CloseableBufferFactory<T extends Buffer> implements
        Factory<CloseableBufferFactory<T>.CloseableBuffer> {

    /**
     * Fluent method the create a CloseableBufferFactory<ByteBuffer>.
     * @param poolInitialSize the initial size of the pool
     * @param bufferSize the size of the buffer
     * @return an instance of CloseableBufferFactory that will handle some {@link ByteBuffer}.
     */
    static CloseableBufferFactory<ByteBuffer> closeableByteBufferFactory(final int poolInitialSize,
            final int bufferSize) {
        return new CloseableBufferFactory<ByteBuffer>(poolInitialSize) {
            @Override
            protected java.nio.ByteBuffer allocate() {
                return ByteBuffer.allocate(bufferSize);
            }
        };
    }

    /**
     * Pool of pre-allocated {@code T} instances, which will grow in size up to the maximum concurrent
     * threads that call this class.
     */
    private final Queue<T> pool;

    CloseableBufferFactory(int poolInitialSize) {
        pool = new ConcurrentLinkedQueue<>();
        for (int i = 0; i < poolInitialSize; ++i) {
            pool.add(allocate());
        }
    }

    /**
     * Any implementation of this class will have to define this method in which the real buffer is allocated.
     * Here are some examples :
     * {@code
     * @Override
     * protected java.nio.CharBuffer allocate() {
     *     return CharBuffer.allocate(1_024);
     * }
     * }
     * or
     * {@code
     * @Override
     * protected java.nio.ByteBuffer allocate() {
     *     return ByteBuffer.allocateDirect(4_096);
     * }
     * }
     * @return
     */
    protected abstract T allocate();

    @Override
    public final CloseableBuffer newInstance() {
        T instance = pool.poll();
        if (instance == null) {
            instance = allocate();
        }
        return new CloseableBuffer(instance);
    }

    final class CloseableBuffer implements AutoCloseable {

        private final T buffer;

        private CloseableBuffer(T buffer) {
            this.buffer = buffer;
        }

        @Override
        public void close() {
            buffer.clear();
            pool.add(buffer);
        }

        T getBuffer() {
            return buffer;
        }
    }
}