package org.araqne.storage.api; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "araqne-rcdirectbuffer-manager") @Provides public class RCDirectBufferManagerImpl implements RCDirectBufferManager { private Logger dbLogger = LoggerFactory.getLogger("A"); private AtomicInteger objCount; private AtomicLong totalCapacity; private Method cleanerMethod = null; private Method cleanMethod = null; private ConcurrentHashMap<String, AtomicLong> poolSizes; private ConcurrentHashMap<String, Long> poolSizeLimits; private ConcurrentHashMap<String, AtomicLong> usageSizes; public static RCDirectBufferManagerImpl getTestManager() { RCDirectBufferManagerImpl manager = new RCDirectBufferManagerImpl(); manager.start(); return manager; } RCDirectBufferManagerImpl() { } public RCDirectBuffer allocateDirect(int capacity, String poolName, String usageName) throws ExceedPoolSizeLimitException { checkPoolCapacity(capacity, poolName, usageName); ByteBuffer buffer = ByteBuffer.allocateDirect(capacity); // update total stat int objcnt = objCount.incrementAndGet(); long totalCap = totalCapacity.addAndGet(buffer.capacity()); RCDirectBuffer ret = new RCDirectBuffer(this, buffer, poolName, usageName); if (dbLogger.isDebugEnabled()) dbLogger.debug("directByteBuffer allocated: {}: {} bytes(total: {} objs, {} bytes)", new Object[] { ret.getOID(), capacity, objcnt, totalCap }); return ret; } private void checkPoolCapacity(int capacity, String poolName, String usageName) throws ExceedPoolSizeLimitException { AtomicLong usageSize = usageSizes.get(usageName); if (usageSize == null) { usageSize = new AtomicLong(0); AtomicLong result = usageSizes.putIfAbsent(usageName, usageSize); if (result != null) usageSize = result; } usageSize.addAndGet(capacity); AtomicLong poolSize = poolSizes.get(poolName); if (poolSize == null) { poolSize = new AtomicLong(0); AtomicLong result = poolSizes.putIfAbsent(poolName, poolSize); if (result != null) { poolSize = result; } } Long poolSizeLimit = poolSizeLimits.get(poolName); if (poolSizeLimit == null) { poolSizeLimit = Long.MAX_VALUE; } Long futurePoolSize = poolSize.addAndGet(capacity); if (futurePoolSize > poolSizeLimit) { poolSize.addAndGet(-capacity); throw new ExceedPoolSizeLimitException(futurePoolSize + capacity, poolSizeLimit); } } public RCDirectBuffer wrap(ByteBuffer buffer, String poolName, String usageName) throws ExceedPoolSizeLimitException { checkPoolCapacity(buffer.capacity(), poolName, usageName); // update total stat int objcnt = objCount.incrementAndGet(); long totalCap = totalCapacity.addAndGet(buffer.capacity()); if (dbLogger.isDebugEnabled()) dbLogger.debug("directByteBuffer wrapped: {}: {} bytes(total: {} objs, {} bytes)", new Object[] { System.identityHashCode(buffer), buffer.capacity(), objcnt, totalCap }); return new RCDirectBuffer(this, buffer, poolName, usageName); } public void setMemoryLimitOfPool(String poolName, Long size) { synchronized (this) { poolSizeLimits.put(poolName, size); } } public RCDirectBuffer allocateDirect(int capacity) { ByteBuffer buffer = ByteBuffer.allocateDirect(capacity); // update total stat int objcnt = objCount.incrementAndGet(); long totalCap = totalCapacity.addAndGet(buffer.capacity()); RCDirectBuffer ret = new RCDirectBuffer(this, buffer, null, null); if (dbLogger.isDebugEnabled()) dbLogger.debug("directByteBuffer allocated: {}: {} bytes(total: {} objs, {} bytes)", new Object[] { ret.getOID(), capacity, objcnt, totalCap }); return ret; } public RCDirectBuffer wrap(ByteBuffer buffer) { // update total stat int objcnt = objCount.incrementAndGet(); long totalCap = totalCapacity.addAndGet(buffer.capacity()); RCDirectBuffer ret = new RCDirectBuffer(this, buffer, null, null); if (dbLogger.isDebugEnabled()) dbLogger.debug("directByteBuffer wrapped: {}: {} bytes(total: {} objs, {} bytes)", new Object[] { ret.getOID(), buffer.capacity(), objcnt, totalCap }); return ret; } @Validate public void start() { poolSizes = new ConcurrentHashMap<String, AtomicLong>(); poolSizeLimits = new ConcurrentHashMap<String, Long>(); usageSizes = new ConcurrentHashMap<String, AtomicLong>(); objCount = new AtomicInteger(0); totalCapacity = new AtomicLong(0); } public long getTotalCapacity() { return totalCapacity.get(); } public int getObjectCount() { return objCount.get(); } public long getUsingPoolSize(String poolName) { return getLong(poolSizes.get(poolName)); } public long getAvailablePoolSize(String poolName) { long using = getUsingPoolSize(poolName); Long limit = poolSizeLimits.get(poolName); if (limit == null) { limit = Long.MAX_VALUE; } return limit - using; } public long getUsingObjectSize(String usageName) { return getLong(usageSizes.get(usageName)); } private long getLong(AtomicLong atomicLong) { if (atomicLong == null) return 0L; else return atomicLong.get(); } public Iterable<String> getPoolNames() { return poolSizes.keySet(); } public Iterable<String> getUsagesNames() { return usageSizes.keySet(); } @Override public void clean(RCDirectBuffer rcbuffer, String poolName, String usageName) { ByteBuffer buffer = rcbuffer.get(); try { int capacity = buffer.capacity(); if (cleanerMethod == null) { cleanerMethod = buffer.getClass().getMethod("cleaner"); cleanerMethod.setAccessible(true); } Object cleaner = cleanerMethod.invoke(buffer); if (cleanMethod == null) { cleanMethod = cleaner.getClass().getMethod("clean"); cleanMethod.setAccessible(true); } cleanMethod.invoke(cleaner); long totalCap = totalCapacity.addAndGet(-capacity); int objcnt = objCount.decrementAndGet(); if (dbLogger.isDebugEnabled()) dbLogger.debug("directByteBuffer destroyed: {}: {} bytes(total: {} objs, {} bytes)", new Object[] { rcbuffer.getOID(), capacity, objcnt, totalCap }); } catch (Throwable t) { dbLogger.warn("directByteBuffer destruction failed: ", t); } finally { long capacity = buffer.capacity(); AtomicLong poolSize = poolSizes.get(poolName); poolSize.addAndGet(-capacity); if (poolSize.compareAndSet(0, 0)) poolSizes.remove(poolName); AtomicLong usageSize = usageSizes.get(usageName); usageSize.addAndGet(-capacity); if (usageSize.compareAndSet(0, 0)) usageSizes.remove(usageName); } } @Override public void clean(ByteBuffer buffer) { try { int capacity = buffer.capacity(); if (cleanerMethod == null) { cleanerMethod = buffer.getClass().getMethod("cleaner"); cleanerMethod.setAccessible(true); } Object cleaner = cleanerMethod.invoke(buffer); if (cleanMethod == null) { cleanMethod = cleaner.getClass().getMethod("clean"); cleanMethod.setAccessible(true); } cleanMethod.invoke(cleaner); long totalCap = totalCapacity.addAndGet(-capacity); int objcnt = objCount.decrementAndGet(); if (dbLogger.isDebugEnabled()) dbLogger.debug("directByteBuffer destroyed: IHC{}: {} bytes(total: {} objs, {} bytes)", new Object[] { System.identityHashCode(buffer), capacity, objcnt, totalCap }); } catch (Throwable t) { dbLogger.warn("directByteBuffer destruction failed: ", t); } } }