Skip to content
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

Open
wants to merge 8 commits into
base: egw-upgrade
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions components/config-util/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,6 @@

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
Expand Down
73 changes: 73 additions & 0 deletions components/framework-axp/pom.xml
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>
269 changes: 269 additions & 0 deletions components/framework-axp/src/main/java/framework/cache/AXPCache.java
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
Copy link
Contributor

@jaadds jaadds Jun 4, 2019

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted

{

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)));
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Author

Choose a reason for hiding this comment

The 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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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();
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to ScheduledExecutorService. it resolves the problem.

}
};
Timer timer = new Timer("Timer");
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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;
}
}
}
Loading