/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.internal.streaming.bytes;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.apache.commons.pool.impl.GenericObjectPool.DEFAULT_MAX_IDLE;
import static org.apache.commons.pool.impl.GenericObjectPool.DEFAULT_MAX_WAIT;
import static org.apache.commons.pool.impl.GenericObjectPool.WHEN_EXHAUSTED_GROW;
import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage;
import static org.slf4j.LoggerFactory.getLogger;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.api.lifecycle.Disposable;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import java.nio.ByteBuffer;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.slf4j.Logger;
/**
* {@link ByteBufferManager} implementation which pools instances for better performance.
* <p>
* Buffers are kept in separate pools depending on their capacity.
* <p>
* Idle buffers and capacity pools are automatically expired.
*
* @since 4.0
*/
public class PoolingByteBufferManager implements ByteBufferManager, Disposable {
private static final Logger LOGGER = getLogger(PoolingByteBufferManager.class);
private static final long ONE_MINUTE = MINUTES.toMillis(1);
private final LoadingCache<Integer, ObjectPool<ByteBuffer>> pools = CacheBuilder.newBuilder()
.expireAfterAccess(1, MINUTES)
.removalListener((RemovalListener<Integer, ObjectPool<ByteBuffer>>) notification -> {
try {
notification.getValue().close();
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Found exception trying to dispose buffer pool for capacity " + notification.getKey(), e);
}
}
}).build(new CacheLoader<Integer, ObjectPool<ByteBuffer>>() {
@Override
public ObjectPool<ByteBuffer> load(Integer capacity) throws Exception {
return createPool(capacity);
}
});
/**
* {@inheritDoc}
*/
@Override
public ByteBuffer allocate(int capacity) {
try {
return pools.getUnchecked(capacity).borrowObject();
} catch (Exception e) {
throw new MuleRuntimeException(createStaticMessage("Could not allocate byte buffer"), e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void deallocate(ByteBuffer byteBuffer) {
int capacity = byteBuffer.capacity();
ObjectPool<ByteBuffer> pool = pools.getIfPresent(capacity);
if (pool != null) {
try {
pool.returnObject(byteBuffer);
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Could not deallocate buffer of capacity " + capacity, e);
}
}
}
}
@Override
public void dispose() {
pools.invalidateAll();
}
private ObjectPool<ByteBuffer> createPool(int capacity) {
GenericObjectPool.Config config = new GenericObjectPool.Config();
config.maxIdle = DEFAULT_MAX_IDLE;
config.maxActive = -1;
config.maxWait = DEFAULT_MAX_WAIT;
config.whenExhaustedAction = WHEN_EXHAUSTED_GROW;
config.minEvictableIdleTimeMillis = ONE_MINUTE;
config.timeBetweenEvictionRunsMillis = ONE_MINUTE;
config.testOnBorrow = false;
config.testOnReturn = false;
config.testWhileIdle = false;
GenericObjectPool genericPool = new GenericObjectPool(new ByteBufferObjectFactory(capacity), config);
return genericPool;
}
private class ByteBufferObjectFactory implements PoolableObjectFactory<ByteBuffer> {
private final int capacity;
private ByteBufferObjectFactory(int capacity) {
this.capacity = capacity;
}
@Override
public ByteBuffer makeObject() throws Exception {
return ByteBuffer.allocate(capacity);
}
@Override
public void destroyObject(ByteBuffer obj) throws Exception {
obj.clear();
}
@Override
public boolean validateObject(ByteBuffer obj) {
return false;
}
@Override
public void activateObject(ByteBuffer obj) throws Exception {
obj.clear();
}
@Override
public void passivateObject(ByteBuffer obj) throws Exception {
}
}
}