Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.cache; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.alfresco.repo.cache.TransactionStats.OpType; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * Simple non-persistent implementation of {@link CacheStatistics}. Statistics * are empty at repository startup. * * @since 5.0 * @author Matt Ward */ public class InMemoryCacheStatistics implements CacheStatistics, ApplicationContextAware { /** Read/Write locks by cache name */ private final ConcurrentMap<String, ReentrantReadWriteLock> locks = new ConcurrentHashMap<>(); private Map<String, Map<OpType, OperationStats>> cacheToStatsMap = new HashMap<>(); private ApplicationContext applicationContext; @Override public long count(String cacheName, OpType opType) { ReadLock readLock = getReadLock(cacheName); readLock.lock(); try { Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName); if (cacheStats == null) { throw new NoStatsForCache(cacheName); } OperationStats opStats = cacheStats.get(opType); return opStats.getCount(); } finally { readLock.unlock(); } } @Override public double meanTime(String cacheName, OpType opType) { ReadLock readLock = getReadLock(cacheName); readLock.lock(); try { Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName); if (cacheStats == null) { throw new NoStatsForCache(cacheName); } OperationStats opStats = cacheStats.get(opType); return opStats.meanTime(); } finally { readLock.unlock(); } } @Override public void add(String cacheName, TransactionStats txStats) { boolean registerCacheStats = false; WriteLock writeLock = getWriteLock(cacheName); writeLock.lock(); try { // Are we adding new stats for a previously unseen cache? registerCacheStats = !cacheToStatsMap.containsKey(cacheName); if (registerCacheStats) { // There are no statistics yet for this cache. cacheToStatsMap.put(cacheName, new HashMap<OpType, OperationStats>()); } Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName); for (OpType opType : OpType.values()) { SummaryStatistics txOpSummary = txStats.getTimings(opType); long count = txOpSummary.getN(); double totalTime = txOpSummary.getSum(); OperationStats oldStats = cacheStats.get(opType); OperationStats newStats; if (oldStats == null) { newStats = new OperationStats(totalTime, count); } else { newStats = new OperationStats(oldStats, totalTime, count); } cacheStats.put(opType, newStats); } } finally { writeLock.unlock(); } if (registerCacheStats) { // We've added stats for a previously unseen cache, raise an event // so that an MBean for the cache may be registered, for example. applicationContext.publishEvent(new CacheStatisticsCreated(this, cacheName)); } } @Override public double hitMissRatio(String cacheName) { ReadLock readLock = getReadLock(cacheName); readLock.lock(); try { Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName); if (cacheStats == null) { throw new NoStatsForCache(cacheName); } long hits = cacheStats.get(OpType.GET_HIT).getCount(); long misses = cacheStats.get(OpType.GET_MISS).getCount(); return (double) hits / (hits + misses); } finally { readLock.unlock(); } } @Override public long numGets(String cacheName) { ReadLock readLock = getReadLock(cacheName); readLock.lock(); try { Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName); if (cacheStats == null) { throw new NoStatsForCache(cacheName); } long hits = cacheStats.get(OpType.GET_HIT).getCount(); long misses = cacheStats.get(OpType.GET_MISS).getCount(); return hits + misses; } finally { readLock.unlock(); } } @Override public Map<OpType, OperationStats> allStats(String cacheName) { ReadLock readLock = getReadLock(cacheName); readLock.lock(); try { Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName); if (cacheStats == null) { throw new NoStatsForCache(cacheName); } return new HashMap<>(cacheStats); } finally { readLock.unlock(); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /** * Gets a {@link ReentrantReadWriteLock} for a specific cache, lazily * creating the lock if necessary. Locks may be created per cache * (rather than hashing to a smaller pool) since the number of * caches is not too large. * * @param cacheName Cache name to obtain lock for. * @return ReentrantReadWriteLock */ private ReentrantReadWriteLock getLock(String cacheName) { if (!locks.containsKey(cacheName)) { ReentrantReadWriteLock newLock = new ReentrantReadWriteLock(); if (locks.putIfAbsent(cacheName, newLock) == null) { // Lock was successfully added to map. return newLock; } ; } return locks.get(cacheName); } private ReadLock getReadLock(String cacheName) { ReadLock readLock = getLock(cacheName).readLock(); return readLock; } private WriteLock getWriteLock(String cacheName) { WriteLock writeLock = getLock(cacheName).writeLock(); return writeLock; } }