blob: cf43f8771b7a97bc0a53033e11f46d9d3b77a8c8 [file] [log] [blame]
/*
* 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.synapse.transport.utils.sslcert.cache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.transport.utils.sslcert.Constants;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Cache Manager takes care of and maintains an LRU cache which implements ManageableCache Interface.
* Duration should be configured such that cacheManager is not too much involved with the cache,
* but manages it optimally.
*/
public class CacheManager {
private static final Log log = LogFactory.getLog(CacheManager.class);
private final boolean DO_NOT_INTERRUPT_IF_RUNNING = false;
private ScheduledExecutorService scheduler;
private ScheduledFuture scheduledFuture = null;
private ManageableCache cache;
private int cacheMaxSize;
private int duration;
private CacheManagingTask cacheManagingTask;
/**
* A new cacheManager will be started on the given ManageableCache object.
*
* @param cache a Manageable Cache which could be managed by this cache manager.
* @param cacheMaxSize Maximum size of the cache. If the cache exceeds this size, LRU values
* will be removed
*/
public CacheManager(ManageableCache cache, int cacheMaxSize, int duration) {
scheduler = Executors.newSingleThreadScheduledExecutor();
this.cache = cache;
this.cacheMaxSize = cacheMaxSize;
this.cacheManagingTask = new CacheManagingTask();
this.duration = duration;
start();
}
/**
* To Start the CacheManager. Should be called only once per CacheManager hence called in
* constructor. CacheManager will run its scheduled task every "duration" number of minutes.
*/
private boolean start() {
if (scheduledFuture == null || (scheduledFuture.isCancelled())) {
scheduledFuture = scheduler.scheduleWithFixedDelay(cacheManagingTask,
duration, duration, TimeUnit.MINUTES);
log.info(cache.getClass().getSimpleName()+" Cache Manager Started");
return true;
}
return false;
}
/**
* Used to wake cacheManager up at will. If this method is called while its task is running, it
* will run its task again soon after its done. CacheManagerTask will be rescheduled as before.
* @return true if successfully waken up. false otherwise.
*/
public boolean wakeUpNow(){
if (scheduledFuture !=null) {
if (!scheduledFuture.isCancelled()) {
scheduledFuture.cancel(DO_NOT_INTERRUPT_IF_RUNNING);
}
scheduledFuture = scheduler.scheduleWithFixedDelay(cacheManagingTask,
0, duration,TimeUnit.MINUTES);
log.info(cache.getClass().getSimpleName()+" Cache Manager Wakened Up.....");
return true;
}
return false;
}
/**
* Change the cacheManager duration (schedule period) to given value.
* @param duration new duration to which the cacheManager schedule period should change.
* @return true if successfully changed. false otherwise.
* @throws IllegalArgumentException if given duration is not between the allowed limit.
*/
public boolean changeDuration(int duration) throws IllegalArgumentException {
int min = Constants.CACHE_MIN_DURATION_MINS;
int max = Constants.CACHE_MAX_DURATION_MINS;
if (duration < min || duration > max) {
throw new IllegalArgumentException("Duration time should should be between " + min +
" and " + max + " minutes");
}
this.duration = duration;
return wakeUpNow();
}
public int getDuration(){
return duration;
}
/**
* Gracefully stop cacheManager.
*/
public boolean stop(){
if (scheduledFuture !=null && !scheduledFuture.isCancelled()){
scheduledFuture.cancel(DO_NOT_INTERRUPT_IF_RUNNING);
log.info(cache.getClass().getSimpleName()+" Cache Manager Stopped.....");
return true;
}
return false;
}
public boolean isRunning() {
return !scheduledFuture.isCancelled();
}
/**
* This is the Scheduled Task the CacheManager uses in order to remove invalid cache values and
* to remove LRU values if the cache reaches cacheMaxSize.
*/
private class CacheManagingTask implements Runnable {
public void run() {
long start = System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug(cache.getClass().getSimpleName() + " Cache Manager Task Started.");
}
ManageableCacheValue nextCacheValue;
//cache.getCacheSize() can vary when new entries are added. So get cache size at this point
int cacheSize = cache.getCacheSize();
int numberToRemove = (cacheSize>cacheMaxSize)? cacheSize - cacheMaxSize: 0;
List<ManageableCacheValue> entriesToRemove = new ArrayList<ManageableCacheValue>();
LRUEntryCollector lruEntryCollector = new LRUEntryCollector(entriesToRemove, numberToRemove);
//Start looking at cache entries from the beginning.
cache.resetIterator();
//Iteration through the cache entries.
while ((cacheSize--) > 0) {
nextCacheValue = cache.getNextCacheValue();
if (nextCacheValue == null) {
log.debug("Cache manager iteration through Cache values done");
break;
}
//Updating invalid cache values
if (!nextCacheValue.isValid()) {
log.debug("Updating Invalid Cache Value by Manager");
nextCacheValue.updateCacheWithNewValue();
}
//There are LRU entries to be removed since cacheSize > maxCacheSize. So collect them.
if (numberToRemove > 0) {
lruEntryCollector.collectEntriesToRemove(nextCacheValue);
}
}
//LRU entries removing
for (ManageableCacheValue oldCacheValue: entriesToRemove) {
log.debug("Removing LRU value from cache");
oldCacheValue.removeThisCacheValue();
}
if (log.isDebugEnabled()) {
log.debug(cache.getClass().getSimpleName()+" Cache Manager Task Done. Took " +
(System.currentTimeMillis() - start) + " ms.");
}
}
private class LRUEntryCollector {
private List<ManageableCacheValue> entriesToRemove;
private int listMaxSize;
LRUEntryCollector(List<ManageableCacheValue> entriesToRemove, int numberToRemove){
this.entriesToRemove = entriesToRemove;
this.listMaxSize = numberToRemove;
}
/**
* This method collects the listMaxSize number of LRU values from the Cache. This is
* called for all the entries in the cache. But only listMaxSize number of LRU entries
* will be collected in entriesToRemove list. These collected values will be removed from
* the cache. This uses a part of the Logic in Insertion Sort.
*
* @param value to be collected if LRU.
*/
private void collectEntriesToRemove(ManageableCacheValue value) {
entriesToRemove.add(value);
int i = entriesToRemove.size() - 1;
for(; i > 0 && (value.getTimeStamp() < entriesToRemove.get(i - 1).getTimeStamp()); i--) {
entriesToRemove.remove(i);
entriesToRemove.add(i,(entriesToRemove.get(i - 1)));
}
entriesToRemove.remove(i);
entriesToRemove.add(i,value);
/*
* First entry in the list will be the oldest. Last will be the earliest in the list.
* So remove the earliest since we need to collect the old (LRU) values to remove
* from cache later
*/
if (entriesToRemove.size() > listMaxSize) {
entriesToRemove.remove(entriesToRemove.size() - 1);
}
}
}
}
}