-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Egw upgrade #166
base: egw-upgrade
Are you sure you want to change the base?
Egw upgrade #166
Changes from 7 commits
7de3487
429dc50
fa7f338
7d1caa4
b61b0d5
c1a3eaa
852d651
788bda9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>core-parent</artifactId> | ||
<groupId>com.wso2telco.core</groupId> | ||
<version>2.4.5-SNAPSHOT</version> | ||
<relativePath>../../pom.xml</relativePath> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>framework-axp</artifactId> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.wso2.carbon</groupId> | ||
<artifactId>org.wso2.carbon.utils</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.google.guava</groupId> | ||
<artifactId>guava</artifactId> | ||
</dependency> | ||
<!--Unit test dependencies --> | ||
<dependency> | ||
<groupId>org.testng</groupId> | ||
<artifactId>testng</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.assertj</groupId> | ||
<artifactId>assertj-core</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.mockito</groupId> | ||
<artifactId>mockito-all</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.easytesting</groupId> | ||
<artifactId>fest-reflect</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.felix</groupId> | ||
<artifactId>maven-scr-plugin</artifactId> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.felix</groupId> | ||
<artifactId>maven-bundle-plugin</artifactId> | ||
<extensions>true</extensions> | ||
<configuration> | ||
<instructions> | ||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> | ||
<Bundle-Name>${project.artifactId}</Bundle-Name> | ||
<Export-Package> | ||
com.wso2telco.framework.* | ||
</Export-Package> | ||
<Import-Package> | ||
javax.servlet;version="2.4.0", | ||
javax.servlet.http;version="2.4.0", | ||
javax.servlet.resources;version="2.4.0", | ||
org.eclipse.equinox.http.helper, | ||
*;resolution:=optional | ||
</Import-Package> | ||
<DynamicImport-Package>*</DynamicImport-Package> | ||
</instructions> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
package framework.cache; | ||
|
||
import java.io.IOException; | ||
import java.lang.ref.SoftReference; | ||
import java.util.Optional; | ||
import java.util.Timer; | ||
import java.util.TimerTask; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
|
||
/** | ||
* Cache implementation for the AXP. | ||
* The cache use a {@link ConcurrentHashMap} for store data in memory. The cache has time-to-live and a purge | ||
* options in side the implementation. Also this has the support for init cache with no time limit. | ||
* This has the capability of the ttl configuration support in object level as well. | ||
* <p> | ||
* Note that in the implementation {@link SoftReference} has used to wrap the object. | ||
* softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError. | ||
* <p> | ||
* !!! HIGH IMPORTANT !!! | ||
* <p> | ||
* All the objects stored in the cache will be return without cloning or deep cloning due to improve the cache performance. | ||
* Because of that user should be responsible of the mutability of the stored object. | ||
*/ | ||
public final class AXPCache implements ICache | ||
{ | ||
|
||
private boolean noTimeLimit; | ||
|
||
private long ttl; | ||
|
||
private static Log log = LogFactory.getLog(AXPCache.class); | ||
|
||
private final ConcurrentHashMap<Object, SoftReference<CacheObject>> cache = new ConcurrentHashMap<>(); | ||
|
||
private static final long DEFAULT_PURGING_TIME = 5 * 60 * 60 * 1000; | ||
|
||
/** | ||
* Initialize cache with the given time to live value. | ||
* Object will be purged after expiring the ttl. This will overide the configuration value defined in the | ||
* | ||
* @param ttl object time-to-live value in milli seconds | ||
*/ | ||
public AXPCache(long ttl) | ||
{ | ||
this.ttl = ttl; | ||
initPurgingTask(this.ttl); | ||
} | ||
|
||
/** | ||
* Does not initialize the cache purging thread if the noTimeLimit parameter is true | ||
* If the given boolean value is false, then the cache purge time will be applicable from the the configuration file. | ||
* Even if the noTimeLimit parameter true, | ||
* | ||
* @param noTimeLimit | ||
*/ | ||
public AXPCache(boolean noTimeLimit) | ||
{ | ||
this.noTimeLimit = noTimeLimit; | ||
if (!noTimeLimit) | ||
{ | ||
ttl = getDefaultTTLFromConfiguration(); | ||
initPurgingTask(ttl); | ||
} | ||
else | ||
{ | ||
ttl = -1; | ||
/** | ||
* if there are any expired objects available in the cache need to clean them. | ||
*/ | ||
initPurgingTask(DEFAULT_PURGING_TIME); | ||
} | ||
} | ||
|
||
/** | ||
* Initialize cache with default purging time. | ||
* Apply the cache object ttl value from the configuration file. | ||
*/ | ||
public AXPCache() | ||
{ | ||
ttl = getDefaultTTLFromConfiguration(); | ||
initPurgingTask(ttl); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @param key | ||
* @param value | ||
* @param <K> | ||
* @param <V> | ||
*/ | ||
@Override | ||
public <K, V> void put(K key, V value) | ||
{ | ||
if (key == null) | ||
{ | ||
return; | ||
} | ||
if (value == null) | ||
{ | ||
cache.remove(key); | ||
} | ||
else | ||
{ | ||
long expiryTime = noTimeLimit ? -1 : System.currentTimeMillis() + ttl; | ||
cache.put(key, new SoftReference<>(new CacheObject(value, expiryTime))); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @param key | ||
* @param value | ||
* @param periodInMillis time-to-live value | ||
* @param <K> | ||
* @param <V> | ||
*/ | ||
@Override | ||
public <K, V> void put(K key, V value, long periodInMillis) | ||
{ | ||
if (key == null) | ||
{ | ||
return; | ||
} | ||
if (value == null) | ||
{ | ||
cache.remove(key); | ||
} | ||
else | ||
{ | ||
long expiryTime = System.currentTimeMillis() + periodInMillis; | ||
cache.put(key, new SoftReference<>(new CacheObject(value, expiryTime))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a SoftReference is fine, but I still think we need to clear HashMap entries using some algorithm. Like an LRU or a LFU algorithm. The reference held by SoftReference which is the CacheObject in this case will be removed, but how about the SoftReference itself? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. implemented another task to clean null references. |
||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @param key | ||
* @param <K> | ||
*/ | ||
@Override | ||
public <K> void remove(K key) | ||
{ | ||
cache.remove(key); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @param key | ||
* @param <K> | ||
* @return | ||
*/ | ||
@Override | ||
public <K> Object get(K key) | ||
{ | ||
return Optional.ofNullable(cache.get(key)).map(SoftReference::get).filter(cacheObject -> !cacheObject.isExpired()) | ||
jaadds marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.map(CacheObject::getValue).orElse(null); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public void clear() | ||
{ | ||
cache.clear(); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @return | ||
*/ | ||
@Override | ||
public long size() | ||
{ | ||
return cache.entrySet().stream().filter(entry -> Optional.ofNullable(entry.getValue()).map(SoftReference::get) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each call to size would go through all entries to check expiry. Can't we simple return the size without explicitly checking for validity, and letting cleanup task do the eviction? |
||
.map(cacheObject -> !cacheObject.isExpired()).orElse(false)).count(); | ||
} | ||
|
||
/** | ||
* Note that if the framework.properties file does not available or entry is not there, -1 will be returned. | ||
* which means cache will not be expired. | ||
* | ||
* @return default ttl configuration in framework.properties file. | ||
*/ | ||
private long getDefaultTTLFromConfiguration() | ||
{ | ||
long ttl; | ||
try | ||
{ | ||
ttl = CachePropertyReader.getInstance().getDefaultTTL() * 1000; | ||
} | ||
catch (IOException e) | ||
{ | ||
ttl = -1; | ||
log.error("Error occurred while reading property file configuration for default cache ttl.", e); | ||
} | ||
return ttl; | ||
} | ||
|
||
private void initPurgingTask(final long ttl) | ||
{ | ||
if (ttl <= 0) | ||
{ | ||
return; | ||
} | ||
TimerTask cleanerTask = new TimerTask() | ||
{ | ||
@Override | ||
public void run() | ||
{ | ||
purge(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if this is prone to a ConcurrentModificationException. If that's the case, timer task handling purge operation can stop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved to ScheduledExecutorService. it resolves the problem. |
||
} | ||
}; | ||
Timer timer = new Timer("Timer"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about replacing this with a ScheduledExecutorService? That way the number of TimerThreads won't increase as the caches grow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
timer.scheduleAtFixedRate(cleanerTask, ttl, ttl); | ||
} | ||
|
||
/** | ||
* Purge objects if the time is expired | ||
*/ | ||
private void purge() | ||
{ | ||
cache.entrySet().removeIf( | ||
entry -> Optional.ofNullable(entry.getValue()).map(SoftReference::get) | ||
.map(CacheObject::isExpired).orElse(false)); | ||
} | ||
|
||
|
||
/** | ||
* Class to hold the cache value and expiration time | ||
*/ | ||
private class CacheObject | ||
{ | ||
|
||
private Object value; | ||
|
||
private long expiryTime; | ||
|
||
/** | ||
* Set the attributes of the cache object | ||
* | ||
* @param value | ||
* @param expiryTime | ||
*/ | ||
public CacheObject(Object value, long expiryTime) | ||
{ | ||
this.value = value; | ||
this.expiryTime = expiryTime; | ||
} | ||
|
||
public Object getValue() | ||
{ | ||
return value; | ||
} | ||
|
||
boolean isExpired() | ||
{ | ||
return expiryTime == -1 ? false : System.currentTimeMillis() > expiryTime; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Occurred that it "may" be possible to support this implementation through javax.cache, by registering AXP as a separate caching provider. Will find more on this and confirm. Nothing to fix on this yet. This comment is to serve as just a note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted