diff --git a/components/config-util/pom.xml b/components/config-util/pom.xml index 391d8466..689f377d 100644 --- a/components/config-util/pom.xml +++ b/components/config-util/pom.xml @@ -37,15 +37,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - 1.7 - 1.7 - - org.apache.felix maven-bundle-plugin diff --git a/components/framework-axp/pom.xml b/components/framework-axp/pom.xml new file mode 100644 index 00000000..a06f15cd --- /dev/null +++ b/components/framework-axp/pom.xml @@ -0,0 +1,73 @@ + + + + core-parent + com.wso2telco.core + 2.4.5-SNAPSHOT + ../../pom.xml + + 4.0.0 + + framework-axp + + + + org.wso2.carbon + org.wso2.carbon.utils + + + com.google.guava + guava + + + + org.testng + testng + + + org.assertj + assertj-core + + + org.mockito + mockito-all + + + org.easytesting + fest-reflect + + + + + + + org.apache.felix + maven-scr-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.groupId}.${project.artifactId} + ${project.artifactId} + + com.wso2telco.framework.* + + + 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 + + * + + + + + + \ No newline at end of file diff --git a/components/framework-axp/src/main/java/framework/cache/AXPCache.java b/components/framework-axp/src/main/java/framework/cache/AXPCache.java new file mode 100644 index 00000000..2fdc1078 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/AXPCache.java @@ -0,0 +1,313 @@ +package framework.cache; + +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.google.common.base.Joiner; +import framework.logging.LazyLogger; +import framework.logging.LazyLoggerFactory; + +/** + * 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. + *

+ * 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. + * There is a separate task to clean cleared soft references from the map. + *

+ * !!! HIGH IMPORTANT !!! + *

+ * 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 +{ + + /** + * five minutes delay between purging task + */ + private static final long DEFAULT_PURGING_TIME = 5 * 60 * 60 * 1000; + + /** + * one minute delay between soft reference cleaning task + */ + private static final long EMPTY_REFERENCE_CLEANING_TASK_DELAY = 60 * 60 * 1000; + + private static LazyLogger log = LazyLoggerFactory.getLogger(AXPCache.class); + + private final ConcurrentHashMap> cache = new ConcurrentHashMap<>(); + + private boolean noTimeLimit; + + private long ttl; + + private ScheduledExecutorService purgeExecutor; + + private ScheduledExecutorService cleanEmptyReferencesExecutor; + + /** + * 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; + initCleaningTasks(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(); + initCleaningTasks(ttl); + } + else + { + ttl = -1; + /** + * if there are any expired objects available in the cache need to clean them. + */ + initCleaningTasks(DEFAULT_PURGING_TIME); + } + } + + /** + * Initialize cache with default purging time. + * Apply the cache object ttl value from the configuration file. + */ + public AXPCache() + { + ttl = getDefaultTTLFromConfiguration(); + initCleaningTasks(ttl); + } + + /** + * {@inheritDoc} + * + * @param key + * @param value + * @param + * @param + */ + @Override + public 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 + * @param + */ + @Override + public 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))); + } + } + + /** + * {@inheritDoc} + * + * @param key + * @param + */ + @Override + public void remove(K key) + { + cache.remove(key); + } + + /** + * {@inheritDoc} + * + * @param key + * @param + * @return + */ + @Override + public Object get(K key) + { + return Optional.ofNullable(cache.get(key)).map(SoftReference::get).filter(cacheObject -> !cacheObject.isExpired()) + .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) + .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 initCleaningTasks(final long ttl) + { + initSoftReferenceCleaningTask(); + initPurgingTask(ttl); + } + + private void initPurgingTask(long ttl) + { + if (ttl <= 0) + { + return; + } + + purgeExecutor = Executors.newSingleThreadScheduledExecutor(); + purgeExecutor.scheduleAtFixedRate(() -> purge(), ttl, ttl, TimeUnit.MILLISECONDS); + } + + private void initSoftReferenceCleaningTask() + { + cleanEmptyReferencesExecutor = Executors.newSingleThreadScheduledExecutor(); + cleanEmptyReferencesExecutor.scheduleAtFixedRate(() -> cleanEmptyReferences(), EMPTY_REFERENCE_CLEANING_TASK_DELAY, + EMPTY_REFERENCE_CLEANING_TASK_DELAY, TimeUnit.MILLISECONDS); + } + + /** + * Purge objects if the time is expired + */ + private void purge() + { + log.debug(() -> Joiner.on(" ").join("Purging cache. class: ", this.getClass().getName())); + cache.entrySet().removeIf( + entry -> Optional.ofNullable(entry.getValue()).map(SoftReference::get) + .map(CacheObject::isExpired).orElse(false)); + } + + /** + * Clean garbage collected soft reference object from the map + */ + private void cleanEmptyReferences() + { + cache.values().removeIf(e -> e.get() == null); + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + + if (purgeExecutor != null) + { + purgeExecutor.shutdown(); + } + if (cleanEmptyReferencesExecutor != null) + { + cleanEmptyReferencesExecutor.shutdown(); + } + } + + /** + * 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; + } + } +} diff --git a/components/framework-axp/src/main/java/framework/cache/AXPCacheBuilder.java b/components/framework-axp/src/main/java/framework/cache/AXPCacheBuilder.java new file mode 100644 index 00000000..4340dc4e --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/AXPCacheBuilder.java @@ -0,0 +1,46 @@ +package framework.cache; + +import framework.cache.impl.DefaultAXPLoadingCache; + +/** + * This builder is responsible for building {@link IAXPLoadingCache} + */ +public class AXPCacheBuilder +{ + + private boolean noTimeLimit = true; + + /** + * @return new {@link AXPCacheBuilder} + */ + public static AXPCacheBuilder newBuilder() + { + return new AXPCacheBuilder(); + } + + /** + * Sets {@code noTimeLimit} flag of {@link AXPCache} + * + * @param noTimeLimit no time limit + * @return current instance of AXPCacheBuilder + */ + public AXPCacheBuilder setNoTimeLimit(final boolean noTimeLimit) + { + this.noTimeLimit = noTimeLimit; + + return this; + } + + /** + * Create and returns new {@link DefaultAXPLoadingCache} by using provided {@code IAXPCacheLoader} + * + * @param cacheLoader cache loader + * @param Type of cache key + * @param Type of cache data + * @return new {@link DefaultAXPLoadingCache} + */ + public IAXPLoadingCache build(final IAXPCacheLoader cacheLoader) + { + return new DefaultAXPLoadingCache<>(cacheLoader, new AXPCache(noTimeLimit)); + } +} diff --git a/components/framework-axp/src/main/java/framework/cache/AXPCacheHandler.java b/components/framework-axp/src/main/java/framework/cache/AXPCacheHandler.java new file mode 100644 index 00000000..22e27500 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/AXPCacheHandler.java @@ -0,0 +1,172 @@ +package framework.cache; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This handler is responsible for managing {@link AXPCache} by using {@link IAXPCacheLoader} + */ +@SuppressWarnings("unchecked") +public final class AXPCacheHandler implements IAXPCacheHandlerService +{ + + private static final IAXPCacheHandlerService INSTANCE = new AXPCacheHandler(); + + private AXPCacheHandler() + { + } + + public static IAXPCacheHandlerService getInstance() + { + return INSTANCE; + } + + /** + * {@inheritDoc} + * + * @param axpCache client cache + * @param cacheLoader cache loader + * @param cacheKey cache key + * @param + * @param + * @return + * @throws Exception + */ + @Override + public V getData(final AXPCache axpCache, final IAXPCacheLoader cacheLoader, final K cacheKey) throws + Exception + { + final V cachedData = (V) axpCache.get(cacheKey); + if (cachedData != null) + { + return cachedData; + } + else + { + Map missingData = cacheLoader.load(Collections.singletonList(cacheKey)); + for (final Map.Entry loadedDataEntry : missingData.entrySet()) + { + axpCache.put(loadedDataEntry.getKey(), loadedDataEntry.getValue()); + } + } + + return (V) axpCache.get(cacheKey); + } + + /** + * {@inheritDoc} + * + * @param axpCache client cache + * @param cacheLoader cache loader + * @param cacheKeyList cache key list + * @param + * @param + * @return + * @throws Exception + */ + @Override + public Map getDataAsMap(final AXPCache axpCache, final IAXPCacheLoader cacheLoader, + final List cacheKeyList) throws Exception + { + final Map dataMap = new HashMap<>(); + final List missingCacheKeyList = new ArrayList<>(); + + for (final K cacheKey : cacheKeyList) + { + final V cachedData = (V) axpCache.get(cacheKey); + + if (cachedData != null) + { + dataMap.put(cacheKey, cachedData); + } + else + { + missingCacheKeyList.add(cacheKey); + } + } + + if (!missingCacheKeyList.isEmpty()) + { + final Map missingDataMap = cacheLoader.load(missingCacheKeyList); + + for (final Map.Entry loadedDataEntry : missingDataMap.entrySet()) + { + axpCache.put(loadedDataEntry.getKey(), loadedDataEntry.getValue()); + } + + dataMap.putAll(missingDataMap); + } + + return dataMap; + } + + /** + * {@inheritDoc} + * + * @param axpCache client cache + * @param cacheLoader cache loader + * @param cacheKeyList cache key list + * @param + * @param + * @return + * @throws Exception + */ + @Override + public List getDataAsList(final AXPCache axpCache, final IAXPCacheLoader cacheLoader, + final List cacheKeyList) throws Exception + { + final Map dataMap = getDataAsMap(axpCache, cacheLoader, cacheKeyList); + + final List resultDataList = new ArrayList<>(); + + for (final K cacheKey : cacheKeyList) + { + resultDataList.add(dataMap.get(cacheKey)); + } + + return resultDataList; + } + + /** + * {@inheritDoc} + * + * @param axpCache client cache + * @param cacheKey cache key + * @param value value + * @param + * @param + */ + @Override + public void update(final AXPCache axpCache, final K cacheKey, final V value) + { + axpCache.put(cacheKey, value); + } + + /** + * {@inheritDoc} + * + * @param axpCache + */ + @Override + public void clear(final AXPCache axpCache) + { + axpCache.clear(); + } + + /** + * {@inheritDoc} + * + * @param axpCache + * @param cacheKey + * @param + */ + @Override + public void remove(final AXPCache axpCache, K cacheKey) + { + axpCache.remove(cacheKey); + } +} diff --git a/components/framework-axp/src/main/java/framework/cache/CachePropertyReader.java b/components/framework-axp/src/main/java/framework/cache/CachePropertyReader.java new file mode 100644 index 00000000..e5dfab09 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/CachePropertyReader.java @@ -0,0 +1,59 @@ +package framework.cache; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +import com.google.common.base.Joiner; +import framework.configuration.CarbonUtilsDelegator; +import framework.configuration.ConfigFile; + +/** + * This class is a dedicated class to read the cache related configurations + */ +class CachePropertyReader +{ + + private static CachePropertyReader instance; + + private String DEFAULT_CACHE_TIMEOUT = "framework.axpcache.timeout"; + + private Properties properties = new Properties(); + + static synchronized CachePropertyReader getInstance() + { + if (instance == null) + { + instance = new CachePropertyReader(); + } + + return instance; + } + + /** + * get default ttl value configured in framework.properties file in conf folder. + * + * @return configured value if available, otherwise -1. + * @throws IOException + */ + long getDefaultTTL() throws IOException + { + if (properties.isEmpty()) + { + String filePath = Joiner.on(File.separator) + .join(CarbonUtilsDelegator.getInstance().getCarbonConfigDirPath(), ConfigFile.FRAMEWORK_CONF.getName()); + Path configPath = Paths.get(filePath); + try (InputStream stream = Files.newInputStream(configPath)) + { + properties.load(stream); + } + } + String property = properties.getProperty(DEFAULT_CACHE_TIMEOUT); + + return property != null && !property.isEmpty() ? Long.parseLong(property) : -1; + } +} diff --git a/components/framework-axp/src/main/java/framework/cache/IAXPCacheHandlerService.java b/components/framework-axp/src/main/java/framework/cache/IAXPCacheHandlerService.java new file mode 100644 index 00000000..9af0c775 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/IAXPCacheHandlerService.java @@ -0,0 +1,86 @@ +package framework.cache; + +import java.util.List; +import java.util.Map; + +/** + * AXP cache handler service. + * This class is responsible of handling axp cache related operations for the loading cache. + */ +public interface IAXPCacheHandlerService +{ + + /** + * Returns the data associated with {@code cacheKey} from provided {@code axpCache}. If the data related to given + * {@code cacheKey} is not in the cache, then the missing data will be loaded according to the logic implemented in + * {@link IAXPCacheLoader#load(List)} and update the provided {@code AXPCache}. + * + * @param axpCache client cache + * @param cacheLoader cache loader + * @param cacheKey cache key + * @param Type of cache key + * @param Type of cached data + * @return data associated with {@code cacheKey} + * @throws Exception + */ + V getData(AXPCache axpCache, IAXPCacheLoader cacheLoader, K cacheKey) + throws Exception; + + /** + * Returns a map of data associated with {@code cacheKeyList} from provided {@code axpCache}. If the data related + * to given {@code cacheKeyList} are not in the cache, then the missing data will be loaded according to the logic + * implemented in {@link IAXPCacheLoader#load(List)} and update the provided {@code axpCache}. + * + * @param axpCache client cache + * @param cacheLoader cache loader + * @param cacheKeyList cache key list + * @param Type of cache key + * @param Type of cached data + * @return a map of data associated with {@code cacheKeyList} + * @throws Exception + */ + Map getDataAsMap(AXPCache axpCache, IAXPCacheLoader cacheLoader, + List cacheKeyList) + throws Exception; + + /** + * Returns a list of data associated with {@code cacheKeyList} from provided {@code axpCache}. If the data related + * to given {@code cacheKeyList} are not in the cache, then the missing data will be loaded according to the logic + * implemented in {@link IAXPCacheLoader#load(List)} and update the provided {@code axpCache}. + *

+ * This always preserve the order of the data list, according to the provided {@code cacheKeyList} + * + * @param axpCache client cache + * @param cacheLoader cache loader + * @param cacheKeyList cache key list + * @param Type of cache key + * @param Type of cached data + * @return a list of data associated with {@code cacheKeyList}, the order will be preserved + * @throws Exception + */ + List getDataAsList(AXPCache axpCache, IAXPCacheLoader cacheLoader, + List cacheKeyList) + throws Exception; + + /** + * Add or update cache entries with provided values in provided client cache + * + * @param axpCache client cache + * @param cacheKey cache key + * @param value value + */ + void update(AXPCache axpCache, K cacheKey, V value); + + /** + * Clear all cached data in provide cache + */ + void clear(AXPCache axpCache); + + /** + * Remove data from the cache associated with the given cache key + * + * @param cacheKey + * @param + */ + void remove(final AXPCache axpCache, K cacheKey); +} diff --git a/components/framework-axp/src/main/java/framework/cache/IAXPCacheLoader.java b/components/framework-axp/src/main/java/framework/cache/IAXPCacheLoader.java new file mode 100644 index 00000000..8cf1d832 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/IAXPCacheLoader.java @@ -0,0 +1,23 @@ +package framework.cache; + +import java.util.List; +import java.util.Map; + +/** + * This defines the contract related to data loading logic of a client side cache + * + * @param Type of cache key + * @param Type of cached data + */ +public interface IAXPCacheLoader +{ + + /** + * Load all the data related to cache key list + * + * @param cacheKeyList cache key list + * @return a map from each key in {@code keys} to the data associated with that key; may not contain null values + * @throws Exception + */ + Map load(List cacheKeyList) throws Exception; +} diff --git a/components/framework-axp/src/main/java/framework/cache/IAXPLoadingCache.java b/components/framework-axp/src/main/java/framework/cache/IAXPLoadingCache.java new file mode 100644 index 00000000..319c1f16 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/IAXPLoadingCache.java @@ -0,0 +1,71 @@ +package framework.cache; + +import java.util.List; +import java.util.Map; + +/** + * This defines the contract related to loading cache. Use {@link AXPCacheBuilder} to define + * and create {@link IAXPLoadingCache} + * + * @param Type of cache key + * @param Type of cached data + */ +public interface IAXPLoadingCache +{ + + /** + * Returns data associated with {@code cacheKey} from the cache. If the data related to given {@code cacheKey} is not + * in the cache, then the missing data will be loaded according to the logic implemented in + * {@link IAXPCacheLoader#load(List)} and update the cache. + * + * @param cacheKey cache key + * @return Returns data associated with {@code cacheKey} from the cache + * @throws Exception + */ + V getData(K cacheKey) throws Exception; + + /** + * Returns a map of data associated with {@code cacheKeyList} from the cache. If the data related to given + * {@code cacheKeyList} are not in the cache, then the missing data will be loaded according to the logic implemented + * in {@link IAXPCacheLoader#load(List)} and update the cache. + * + * @param cacheKeyList cache key list + * @return Returns a map of data associated with {@code cacheKeyList} from the cache + * @throws Exception + */ + Map getDataAsMap(List cacheKeyList) throws Exception; + + /** + * Returns a list of data associated with {@code cacheKeyList} from the cache. If the data related to given + * {@code cacheKeyList} are not in the cache, then the missing data will be loaded according to the logic implemented + * in {@link IAXPCacheLoader#load(List)} and update the cache. + *

+ * This always preserve the order of the data list, according to the provided {@code cacheKeyList} + * + * @param cacheKeyList cache key list + * @return Returns a list of data associated with {@code cacheKeyList} from the cache, the order will be preserved + * @throws Exception + */ + List getDataAsList(List cacheKeyList) throws Exception; + + /** + * Add or update cache entries with provided values + * + * @param cacheKey cache key + * @param value value + */ + void update(K cacheKey, V value); + + /** + * remove data from the cache associated with the given cache key + * + * @param cacheKey + */ + void remove(K cacheKey); + + /** + * Clear all cached data + */ + void clear(); + +} diff --git a/components/framework-axp/src/main/java/framework/cache/ICache.java b/components/framework-axp/src/main/java/framework/cache/ICache.java new file mode 100644 index 00000000..0e7810c7 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/ICache.java @@ -0,0 +1,59 @@ +package framework.cache; + +/** + * The interface for the cache. + * This has the support for all the primary features of a cache + */ +public interface ICache +{ + + /** + * Insert data in to the cache with custom time-to-live value. + * + * @param key + * @param value + * @param periodInMillis time-to-live value + * @param + * @param + */ + void put(K key, V value, long periodInMillis); + + /** + * Remove the data associated with the given key from the cache. + * + * @param key + * @param + */ + void remove(K key); + + /** + * Get the data associated with the the given key. + * + * @param key + * @param + * @return the object associated to the given cache key + */ + Object get(K key); + + /** + * Get the current size of the cache. + * + * @return current size of the cache. + */ + long size(); + + /** + * Insert data into the cache against the given key. + * + * @param key + * @param value + * @param + * @param + */ + void put(K key, V value); + + /** + * Clear all the data in the cache + */ + void clear(); +} diff --git a/components/framework-axp/src/main/java/framework/cache/impl/DefaultAXPLoadingCache.java b/components/framework-axp/src/main/java/framework/cache/impl/DefaultAXPLoadingCache.java new file mode 100644 index 00000000..fcc70b2f --- /dev/null +++ b/components/framework-axp/src/main/java/framework/cache/impl/DefaultAXPLoadingCache.java @@ -0,0 +1,99 @@ +package framework.cache.impl; + + +import java.util.List; +import java.util.Map; + +import framework.cache.AXPCache; +import framework.cache.AXPCacheHandler; +import framework.cache.IAXPCacheLoader; +import framework.cache.IAXPLoadingCache; + +/** + * This implementation uses {@link AXPCache} as the base cache. + */ +public class DefaultAXPLoadingCache implements IAXPLoadingCache +{ + + private final AXPCache axpCache; + + private final IAXPCacheLoader cacheLoader; + + public DefaultAXPLoadingCache(final IAXPCacheLoader cacheLoader, + final AXPCache axpCache) + { + this.axpCache = axpCache; + this.cacheLoader = cacheLoader; + } + + /** + * {@inheritDoc} + * + * @param cacheKey cache key + * @return + * @throws Exception + */ + @Override + public V getData(final K cacheKey) throws Exception + { + return AXPCacheHandler.getInstance().getData(axpCache, cacheLoader, cacheKey); + } + + /** + * {@inheritDoc} + * + * @param cacheKeyList cache key list + * @return + * @throws Exception + */ + @Override + public Map getDataAsMap(final List cacheKeyList) throws Exception + { + return AXPCacheHandler.getInstance().getDataAsMap(axpCache, cacheLoader, cacheKeyList); + } + + /** + * {@inheritDoc} + * + * @param cacheKeyList cache key list + * @return + * @throws Exception + */ + @Override + public List getDataAsList(final List cacheKeyList) throws Exception + { + return AXPCacheHandler.getInstance().getDataAsList(axpCache, cacheLoader, cacheKeyList); + } + + /** + * {@inheritDoc} + * + * @param cacheKey cache key + * @param value value + */ + @Override + public void update(final K cacheKey, final V value) + { + AXPCacheHandler.getInstance().update(axpCache, cacheKey, value); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() + { + AXPCacheHandler.getInstance().clear(axpCache); + } + + /** + * {@inheritDoc} + * + * @param cacheKey + */ + @Override + public void remove(K cacheKey) + { + AXPCacheHandler.getInstance().remove(axpCache, cacheKey); + } +} diff --git a/components/framework-axp/src/main/java/framework/configuration/CarbonUtilsDelegator.java b/components/framework-axp/src/main/java/framework/configuration/CarbonUtilsDelegator.java new file mode 100644 index 00000000..2a27d620 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/CarbonUtilsDelegator.java @@ -0,0 +1,41 @@ +package framework.configuration; + +import org.wso2.carbon.utils.CarbonUtils; + +/** + * Delegator class for {@link CarbonUtils} + */ +public class CarbonUtilsDelegator +{ + + private static final CarbonUtilsDelegator INSTANCE = new CarbonUtilsDelegator(); + + public static CarbonUtilsDelegator getInstance() + { + return INSTANCE; + } + + private CarbonUtilsDelegator() + { + } + + /** + * Get carbon home directory path as a string + * + * @return + */ + public String getCarbonHome() + { + return CarbonUtils.getCarbonHome(); + } + + /** + * Get Carbon configuration directory path as a string + * + * @return + */ + public String getCarbonConfigDirPath() + { + return CarbonUtils.getCarbonConfigDirPath(); + } +} diff --git a/components/framework-axp/src/main/java/framework/configuration/ConfigFile.java b/components/framework-axp/src/main/java/framework/configuration/ConfigFile.java new file mode 100644 index 00000000..4a6e8752 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/ConfigFile.java @@ -0,0 +1,41 @@ +package framework.configuration; + +/** + * enum to hold the configuration file names which contains in the default config directory. + */ +public enum ConfigFile +{ + + /** + * oneapi-validation-conf.properties + */ + ONEAPI_VALIDATION_CONF("oneapi-validation-conf.properties"), + + /** + * mediator-conf.properties + */ + MEDIATOR_CONF("mediator-conf.properties"), + + /** + * framework.properties + */ + FRAMEWORK_CONF("framework.properties"), + + /** + * MobileCountryConfig.xml + */ + MOBILE_COUNTRY_CONF("MobileCountryConfig.xml"); + + + private final String name; + + ConfigFile(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } +} diff --git a/components/framework-axp/src/main/java/framework/configuration/ConfigFileReaderStrategy.java b/components/framework-axp/src/main/java/framework/configuration/ConfigFileReaderStrategy.java new file mode 100644 index 00000000..477f055b --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/ConfigFileReaderStrategy.java @@ -0,0 +1,51 @@ +package framework.configuration; + +import framework.configuration.impl.JavaPropertyFileReadStrategy; +import framework.configuration.impl.XMLPropertyFileReadStrategy; + +/** + * The class to resolve file reader strategy by extension + */ +public final class ConfigFileReaderStrategy +{ + + private static final ConfigFileReaderStrategy INSTANCE = new ConfigFileReaderStrategy(); + + private static final String EXTENSION_PROPERTIES = "properties"; + + private ConfigFileReaderStrategy() + { + } + + private static final String EXTENSION_XML = "xml"; + + /** + * get singleton instance of {@link ConfigFileReaderStrategy} + * + * @return + */ + public static ConfigFileReaderStrategy getInstance() + { + return INSTANCE; + } + + /** + * Get the file read strategy by file extension + * + * @param extension + * @return + */ + public IConfigFileReaderStrategy getStrategy(String extension) + { + switch (extension) + { + case EXTENSION_PROPERTIES: + return new JavaPropertyFileReadStrategy(CarbonUtilsDelegator.getInstance()); + case EXTENSION_XML: + return new XMLPropertyFileReadStrategy(); + default: + //TODO introduce new runtime exception + throw new RuntimeException("Unsupported file type."); + } + } +} diff --git a/components/framework-axp/src/main/java/framework/configuration/IConfigFileReaderService.java b/components/framework-axp/src/main/java/framework/configuration/IConfigFileReaderService.java new file mode 100644 index 00000000..e6ed1ea7 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/IConfigFileReaderService.java @@ -0,0 +1,19 @@ +package framework.configuration; + +import java.util.Map; + +/** + * The class for read configuration files in config directory. + */ +public interface IConfigFileReaderService +{ + + /** + * Read the given file in the default config directory. + * + * @param configFile + * @return + * @throws Exception + */ + Map readFile(ConfigFile configFile) throws Exception; +} diff --git a/components/framework-axp/src/main/java/framework/configuration/IConfigFileReaderStrategy.java b/components/framework-axp/src/main/java/framework/configuration/IConfigFileReaderStrategy.java new file mode 100644 index 00000000..175ca147 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/IConfigFileReaderStrategy.java @@ -0,0 +1,20 @@ +package framework.configuration; + +import java.io.IOException; +import java.util.Map; + +/** + * The class for Configuration files read services. + */ +public interface IConfigFileReaderStrategy +{ + + /** + * Read the given configuration file from the default configuration directory. + * + * @param fileName + * @return a map of properties which contains in the given file + * @throws IOException + */ + Map readFile(String fileName) throws IOException; +} diff --git a/components/framework-axp/src/main/java/framework/configuration/impl/ConfigFileReaderServiceImpl.java b/components/framework-axp/src/main/java/framework/configuration/impl/ConfigFileReaderServiceImpl.java new file mode 100644 index 00000000..8d6aa849 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/impl/ConfigFileReaderServiceImpl.java @@ -0,0 +1,82 @@ +package framework.configuration.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import framework.cache.AXPCacheBuilder; +import framework.cache.IAXPCacheLoader; +import framework.cache.IAXPLoadingCache; +import framework.configuration.ConfigFile; +import framework.configuration.ConfigFileReaderStrategy; +import framework.configuration.IConfigFileReaderService; +import framework.configuration.IConfigFileReaderStrategy; + +/** + * Implementation for the {@link IConfigFileReaderService} + * This file reader class will help to read configuration property files with a caching support. + */ +public class ConfigFileReaderServiceImpl implements IConfigFileReaderService +{ + + private static final ConfigFileReaderServiceImpl INSTANCE = new ConfigFileReaderServiceImpl(); + + private static final String DOT = "."; + + private IAXPLoadingCache> axpLoadingCache = AXPCacheBuilder.newBuilder() + .setNoTimeLimit(true).build(new PropertyFileCacheLoader()); + + private ConfigFileReaderServiceImpl() + { + } + + /** + * Get Singleton instance of {@link ConfigFileReaderServiceImpl} + * + * @return + */ + public static ConfigFileReaderServiceImpl getInstance() + { + return INSTANCE; + } + + /** + * {@inheritDoc} + * + * @param configFile + * @return + * @throws Exception + */ + @Override + public Map readFile(ConfigFile configFile) throws Exception + { + return axpLoadingCache.getData(configFile.getName()); + } + + class PropertyFileCacheLoader implements IAXPCacheLoader> + { + + /** + * {@inheritDoc} + * + * @param cacheKeyList cache key list + * @return + * @throws Exception + */ + @Override + public Map> load(List cacheKeyList) throws Exception + { + ConfigFileReaderStrategy configFileReaderStrategy = ConfigFileReaderStrategy.getInstance(); + Map> filePropertiesMap = new HashMap<>(); + for (String fileName : cacheKeyList) + { + IConfigFileReaderStrategy strategy = configFileReaderStrategy + .getStrategy(fileName.substring(fileName.lastIndexOf(DOT) + 1)); + Map propertiesMap = strategy.readFile(fileName); + filePropertiesMap.put(fileName, propertiesMap); + } + + return filePropertiesMap; + } + } +} diff --git a/components/framework-axp/src/main/java/framework/configuration/impl/JavaPropertyFileReadStrategy.java b/components/framework-axp/src/main/java/framework/configuration/impl/JavaPropertyFileReadStrategy.java new file mode 100644 index 00000000..99c5b7cd --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/impl/JavaPropertyFileReadStrategy.java @@ -0,0 +1,51 @@ +package framework.configuration.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Properties; + +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import framework.configuration.CarbonUtilsDelegator; +import framework.configuration.IConfigFileReaderStrategy; + +/** + * Java property file reader strategy. + * This supports for the standard java property files. + */ +public final class JavaPropertyFileReadStrategy implements IConfigFileReaderStrategy +{ + + private CarbonUtilsDelegator carbonUtilsDelegator; + + public JavaPropertyFileReadStrategy(CarbonUtilsDelegator carbonUtilsDelegator) + { + this.carbonUtilsDelegator = carbonUtilsDelegator; + } + + /** + * {@inheritDoc} + * + * @param fileName + * @return + * @throws IOException + */ + @Override + public Map readFile(String fileName) throws IOException + { + String filePath = Joiner.on(File.separator).join(carbonUtilsDelegator.getCarbonConfigDirPath(), fileName); + Path configPath = Paths.get(filePath); + try (InputStream stream = Files.newInputStream(configPath)) + { + Properties properties = new Properties(); + properties.load(stream); + + return Maps.fromProperties(properties); + } + } +} \ No newline at end of file diff --git a/components/framework-axp/src/main/java/framework/configuration/impl/XMLPropertyFileReadStrategy.java b/components/framework-axp/src/main/java/framework/configuration/impl/XMLPropertyFileReadStrategy.java new file mode 100644 index 00000000..71022eb4 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/configuration/impl/XMLPropertyFileReadStrategy.java @@ -0,0 +1,20 @@ +package framework.configuration.impl; + +import java.util.Map; + +import framework.configuration.IConfigFileReaderStrategy; + +/** + * XML property file reader strategy + * This supports for the xml property files. + */ +public final class XMLPropertyFileReadStrategy implements IConfigFileReaderStrategy +{ + + @Override + public Map readFile(String fileName) + { + //TODO + return null; + } +} diff --git a/components/framework-axp/src/main/java/framework/logging/LazyLogger.java b/components/framework-axp/src/main/java/framework/logging/LazyLogger.java new file mode 100644 index 00000000..00e8cc5c --- /dev/null +++ b/components/framework-axp/src/main/java/framework/logging/LazyLogger.java @@ -0,0 +1,110 @@ +package framework.logging; + +import java.util.function.Supplier; + +/** + * Facade to support application logging. + * following log level operation support is available through this class. + *

+ * * TRACE + * * INFO + * * DEBUG + * * WARN + * * ERROR + * * FATAL + *

+ * Create an instance using {@link LazyLoggerFactory#getLogger(Class)} + */ +public interface LazyLogger +{ + + /** + * Log a message object with the TRACE level. + * + * @param supplier function to get the message object + */ + void trace(Supplier supplier); + + /** + * Log a message object with the TRACE level including the stack trace of the Throwable t passed as parameter + * + * @param supplier function to get the message object + * @param t + */ + void trace(Supplier supplier, Throwable t); + + /** + * Log a message object with the INFO level. + * + * @param supplier function to get the message object + */ + void info(Supplier supplier); + + /** + * Log a message object with the INFO level including the stack trace of the Throwable t passed as parameter + * + * @param supplier function to get the message object + * @param t + */ + void info(Supplier supplier, Throwable t); + + /** + * Log a message object with the DEBUG level. + * + * @param supplier function to get the message object + */ + void debug(Supplier supplier); + + /** + * Log a message object with the DEBUG level including the stack trace of the Throwable t passed as parameter + * + * @param supplier function to get the message object + * @param t + */ + void debug(Supplier supplier, Throwable t); + + /** + * Log a message object with the WARN level. + * + * @param message + */ + void warn(Object message); + + /** + * Log a message object with the WARN level including the stack trace of the Throwable t passed as parameter + * + * @param message + * @param t + */ + void warn(Object message, Throwable t); + + /** + * Log a message object with the ERROR level. + * + * @param message + */ + void error(Object message); + + /** + * Log a message object with the ERROR level including the stack trace of the Throwable t passed as parameter + * + * @param message + * @param t + */ + void error(Object message, Throwable t); + + /** + * Log a message object with the FATAL level. + * + * @param message + */ + void fatal(Object message); + + /** + * Log a message object with the FATAL level including the stack trace of the Throwable t passed as parameter + * + * @param message + * @param t + */ + void fatal(Object message, Throwable t); +} diff --git a/components/framework-axp/src/main/java/framework/logging/LazyLoggerFactory.java b/components/framework-axp/src/main/java/framework/logging/LazyLoggerFactory.java new file mode 100644 index 00000000..54da4ffc --- /dev/null +++ b/components/framework-axp/src/main/java/framework/logging/LazyLoggerFactory.java @@ -0,0 +1,30 @@ +package framework.logging; + +/** + * Factory class to create a {@link LazyLogger} instance + */ +public final class LazyLoggerFactory +{ + + /** + * Get {@link LazyLogger} instance + * + * @param clazz + * @return + */ + public static LazyLogger getLogger(Class clazz) + { + return new LazyLoggerImpl(clazz); + } + + /** + * Get {@link LazyLogger} instance + * + * @param name + * @return + */ + public static LazyLogger getLogger(String name) + { + return new LazyLoggerImpl(name); + } +} diff --git a/components/framework-axp/src/main/java/framework/logging/LazyLoggerImpl.java b/components/framework-axp/src/main/java/framework/logging/LazyLoggerImpl.java new file mode 100644 index 00000000..9943c018 --- /dev/null +++ b/components/framework-axp/src/main/java/framework/logging/LazyLoggerImpl.java @@ -0,0 +1,182 @@ +package framework.logging; + +import java.util.function.Supplier; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +/** + * Implementation for the {@link LazyLogger} + * This implementation is based on the {@link Logger} + */ +final class LazyLoggerImpl implements LazyLogger +{ + + private final Logger logger; + + protected LazyLoggerImpl(Class clazz) + { + logger = LogManager.getLogger(clazz); + } + + protected LazyLoggerImpl(String name) + { + logger = LogManager.getLogger(name); + } + + /** + * {@inheritDoc} + * + * @param supplier function to get the message object + */ + @Override + public void trace(Supplier supplier) + { + if (logger.isTraceEnabled()) + { + logger.trace(supplier.get()); + } + } + + /** + * {@inheritDoc} + * + * @param supplier function to get the message object + * @param t + */ + @Override + public void trace(Supplier supplier, Throwable t) + { + if (logger.isTraceEnabled()) + { + logger.trace(supplier.get(), t); + } + } + + /** + * {@inheritDoc} + * + * @param supplier function to get the message object + */ + @Override + public void info(Supplier supplier) + { + if (logger.isInfoEnabled()) + { + logger.info(supplier.get()); + } + } + + /** + * {@inheritDoc} + * + * @param supplier function to get the message object + * @param t + */ + @Override + public void info(Supplier supplier, Throwable t) + { + if (logger.isInfoEnabled()) + { + logger.info(supplier.get(), t); + } + } + + /** + * {@inheritDoc} + * + * @param supplier function to get the message object + */ + @Override + public void debug(Supplier supplier) + { + if (logger.isDebugEnabled()) + { + logger.debug(supplier.get()); + } + } + + /** + * {@inheritDoc} + * + * @param supplier function to get the message object + * @param t + */ + @Override + public void debug(Supplier supplier, Throwable t) + { + if (logger.isDebugEnabled()) + { + logger.debug(supplier.get(), t); + } + } + + /** + * {@inheritDoc} + * + * @param message + */ + @Override + public void warn(Object message) + { + logger.warn(message); + } + + /** + * {@inheritDoc} + * + * @param message + * @param t + */ + @Override + public void warn(Object message, Throwable t) + { + logger.warn(message, t); + } + + /** + * {@inheritDoc} + * + * @param message + */ + @Override + public void error(Object message) + { + logger.error(message); + } + + /** + * {@inheritDoc} + * + * @param message + * @param t + */ + @Override + public void error(Object message, Throwable t) + { + logger.error(message); + } + + /** + * {@inheritDoc} + * + * @param message + */ + @Override + public void fatal(Object message) + { + logger.fatal(message); + } + + /** + * {@inheritDoc} + * + * @param message + * @param t + */ + @Override + public void fatal(Object message, Throwable t) + { + logger.fatal(message, t); + } +} diff --git a/components/framework-axp/src/test/java/framework/cache/AXPCacheBuilderTest.java b/components/framework-axp/src/test/java/framework/cache/AXPCacheBuilderTest.java new file mode 100644 index 00000000..4a13dabc --- /dev/null +++ b/components/framework-axp/src/test/java/framework/cache/AXPCacheBuilderTest.java @@ -0,0 +1,29 @@ +package framework.cache; + +import framework.cache.impl.DefaultAXPLoadingCache; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.fail; + +public class AXPCacheBuilderTest +{ + + @Test + public void testBuild() + { + try + { + final IAXPLoadingCache clientLoadingCache = AXPCacheBuilder.newBuilder().setNoTimeLimit(true) + .build(cacheKeyList -> null); + + assertThat(clientLoadingCache).isInstanceOf(DefaultAXPLoadingCache.class); + } + catch (Exception e) + { + e.printStackTrace(); + + fail(); + } + } +} diff --git a/components/framework-axp/src/test/java/framework/cache/AXPCacheHandlerTest.java b/components/framework-axp/src/test/java/framework/cache/AXPCacheHandlerTest.java new file mode 100644 index 00000000..ac77a2aa --- /dev/null +++ b/components/framework-axp/src/test/java/framework/cache/AXPCacheHandlerTest.java @@ -0,0 +1,211 @@ +package framework.cache; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.beust.jcommander.internal.Lists; +import org.assertj.core.api.Assertions; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyList; +import static org.mockito.Mockito.*; +import static org.testng.Assert.fail; + +public class AXPCacheHandlerTest +{ + + + private AXPCache axpCache; + + private TestCacheLoader mockCacheLoader; + + @BeforeMethod + public void setUp() + { + axpCache = new AXPCache(true); + axpCache.put(1, "1*"); + axpCache.put(2, "2"); + axpCache.put(3, "3*"); + + mockCacheLoader = mock(TestCacheLoader.class); + } + + @Test + public void testGetDataAsMap() + { + try + { + final List keyList = Lists.newArrayList(); + keyList.add(1); + keyList.add(4); + keyList.add(3); + keyList.add(5); + + when(mockCacheLoader.load(anyList())).thenCallRealMethod(); + + Map dataMap = + AXPCacheHandler.getInstance().getDataAsMap(axpCache, mockCacheLoader, keyList); + + assertThat(dataMap).isNotNull(); + assertThat(dataMap.size()).isEqualTo(4); + assertThat(dataMap.get(1)).isEqualTo("1*"); + assertThat(dataMap.get(4)).isEqualTo("4"); + assertThat(dataMap.get(3)).isEqualTo("3*"); + assertThat(dataMap.get(5)).isEqualTo("5"); + + assertThat(axpCache.size()).isEqualTo(5); + assertThat(axpCache.get(1)).isEqualTo("1*"); + assertThat(axpCache.get(2)).isEqualTo("2"); + assertThat(axpCache.get(3)).isEqualTo("3*"); + assertThat(axpCache.get(4)).isEqualTo("4"); + assertThat(axpCache.get(5)).isEqualTo("5"); + + verify(mockCacheLoader, only()).load(anyList()); + + dataMap = AXPCacheHandler.getInstance().getDataAsMap(axpCache, mockCacheLoader, keyList); + + Assertions.assertThat(dataMap).isNotNull(); + assertThat(dataMap.size()).isEqualTo(4); + assertThat(dataMap.get(1)).isEqualTo("1*"); + assertThat(dataMap.get(4)).isEqualTo("4"); + assertThat(dataMap.get(3)).isEqualTo("3*"); + assertThat(dataMap.get(5)).isEqualTo("5"); + + assertThat(axpCache.size()).isEqualTo(5); + assertThat(axpCache.get(1)).isEqualTo("1*"); + assertThat(axpCache.get(2)).isEqualTo("2"); + assertThat(axpCache.get(3)).isEqualTo("3*"); + assertThat(axpCache.get(4)).isEqualTo("4"); + assertThat(axpCache.get(5)).isEqualTo("5"); + + verify(mockCacheLoader, only()).load(anyList()); + } + catch (Exception e) + { + e.printStackTrace(); + + fail(); + } + } + + @Test + public void testGetDataAsList() + { + try + { + final List keyList = Lists.newArrayList(); + keyList.add(1); + keyList.add(4); + keyList.add(3); + keyList.add(5); + + when(mockCacheLoader.load(anyList())).thenCallRealMethod(); + + List dataList = + AXPCacheHandler.getInstance().getDataAsList(axpCache, mockCacheLoader, keyList); + + assertThat(dataList).isNotNull(); + assertThat(dataList.size()).isEqualTo(4); + assertThat(dataList.get(0)).isEqualTo("1*"); + assertThat(dataList.get(1)).isEqualTo("4"); + assertThat(dataList.get(2)).isEqualTo("3*"); + assertThat(dataList.get(3)).isEqualTo("5"); + + assertThat(axpCache.size()).isEqualTo(5); + assertThat(axpCache.get(1)).isEqualTo("1*"); + assertThat(axpCache.get(2)).isEqualTo("2"); + assertThat(axpCache.get(3)).isEqualTo("3*"); + assertThat(axpCache.get(4)).isEqualTo("4"); + assertThat(axpCache.get(5)).isEqualTo("5"); + + verify(mockCacheLoader, only()).load(anyList()); + + dataList = AXPCacheHandler.getInstance().getDataAsList(axpCache, mockCacheLoader, keyList); + + assertThat(dataList).isNotNull(); + assertThat(dataList.size()).isEqualTo(4); + assertThat(dataList.get(0)).isEqualTo("1*"); + assertThat(dataList.get(1)).isEqualTo("4"); + assertThat(dataList.get(2)).isEqualTo("3*"); + assertThat(dataList.get(3)).isEqualTo("5"); + + assertThat(axpCache.size()).isEqualTo(5); + assertThat(axpCache.get(1)).isEqualTo("1*"); + assertThat(axpCache.get(2)).isEqualTo("2"); + assertThat(axpCache.get(3)).isEqualTo("3*"); + assertThat(axpCache.get(4)).isEqualTo("4"); + assertThat(axpCache.get(5)).isEqualTo("5"); + + verify(mockCacheLoader, only()).load(anyList()); + } + catch (Exception e) + { + e.printStackTrace(); + + fail(); + } + } + + @Test + public void testGetData_cached() throws Exception + { + String result = AXPCacheHandler.getInstance().getData(axpCache, mockCacheLoader, 1); + assertThat(result).isEqualTo("1*"); + } + + @Test + public void testGetData_nonCached() throws Exception + { + List keys = Lists.newArrayList(); + keys.add(7); + when(mockCacheLoader.load(keys)).thenCallRealMethod(); + String result = AXPCacheHandler.getInstance().getData(axpCache, mockCacheLoader, 7); + assertThat(result).isEqualTo("7"); + } + + @Test + public void testUpdate() + { + int key = 2; + AXPCacheHandler.getInstance().update(axpCache, key, "updated value"); + assertThat(axpCache.get(key)).isEqualTo("updated value"); + } + + @Test + public void testClearCache() + { + AXPCache cache = new AXPCache(true); + cache.put(1, 1L); + cache.put(2, 2L); + cache.put(3, 3L); + + AXPCacheHandler.getInstance().clear(cache); + assertThat(cache.get(1l)).isNull(); + assertThat(cache.get(2l)).isNull(); + assertThat(cache.get(3l)).isNull(); + + assertThat(cache.get(1)).isNull(); + assertThat(cache.get(2)).isNull(); + assertThat(cache.get(3)).isNull(); + } + + private static class TestCacheLoader implements IAXPCacheLoader + { + + @Override + public Map load(List cacheKeyList) throws Exception + { + final Map dataMap = new HashMap<>(); + + for (Integer testCacheKey : cacheKeyList) + { + dataMap.put(testCacheKey, testCacheKey + ""); + } + + return dataMap; + } + } +} diff --git a/components/framework-axp/src/test/java/framework/cache/AXPCacheTest.java b/components/framework-axp/src/test/java/framework/cache/AXPCacheTest.java new file mode 100644 index 00000000..1147639e --- /dev/null +++ b/components/framework-axp/src/test/java/framework/cache/AXPCacheTest.java @@ -0,0 +1,168 @@ +package framework.cache; + +import org.assertj.core.api.Assertions; +import org.testng.annotations.Test; + +import static org.testng.Assert.fail; + +public class AXPCacheTest +{ + + private ICache cache; + + @Test + public void testPut() + { + cache = new AXPCache(true); + cache.put("key1", "value1"); + cache.put("key2", "value2"); + cache.put("key3", "value3"); + + Assertions.assertThat((String) cache.get("key1")).isEqualTo("value1"); + Assertions.assertThat((String) cache.get("key2")).isEqualTo("value2"); + Assertions.assertThat((String) cache.get("key3")).isEqualTo("value3"); + } + + @Test + public void testPut_withNullKey() + { + cache = new AXPCache(true); + cache.put(null, "value1"); + cache.put("key2", "value2"); + cache.put("key3", "value3"); + + Assertions.assertThat(cache.size()).isEqualTo(2); + } + + @Test + public void testPut_withNullValue() + { + cache = new AXPCache(true); + cache.put("key1", "value1"); + cache.put("key2", "value2"); + cache.put("key3", "value3"); + + //Remove inserted value + cache.put("key1", null); + + Assertions.assertThat(cache.size()).isEqualTo(2); + Assertions.assertThat((String) cache.get("key1")).isNull(); + Assertions.assertThat((String) cache.get("key2")).isEqualTo("value2"); + Assertions.assertThat((String) cache.get("key3")).isEqualTo("value3"); + } + + @Test + public void testGet_withTTL() + { + try + { + Thread t = new Thread(() -> { + cache = new AXPCache(100); + cache.put("key1", "value1"); + cache.put("key2", "value2"); + cache.put("key3", "value3"); + + try + { + //Sleep until cache get cleared + Thread.sleep(120); + } + catch (InterruptedException e) + { + e.printStackTrace(); + + fail(); + } + Assertions.assertThat(cache.get("key1")).isNull(); + Assertions.assertThat(cache.get("key2")).isNull(); + Assertions.assertThat(cache.get("key3")).isNull(); + }); + t.start(); + t.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + + fail(); + } + } + + @Test + public void testPut_withTTL() + { + try + { + Thread t = new Thread(() -> { + cache = new AXPCache(true); + cache.put("key1", "value1", 100); + cache.put("key2", "value2", 100); + cache.put("key3", "value3", 100); + + try + { + //Sleep until cache get cleared + Thread.sleep(120); + } + catch (InterruptedException e) + { + e.printStackTrace(); + + fail(); + } + Assertions.assertThat(cache.get("key1")).isNull(); + Assertions.assertThat(cache.get("key2")).isNull(); + Assertions.assertThat(cache.get("key3")).isNull(); + }); + t.start(); + t.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + + fail(); + } + } + + @Test + public void testRemove() + { + cache = new AXPCache(true); + cache.put("key1", "value1", 100); + cache.put("key2", "value2", 100); + cache.put("key3", "value3", 100); + + cache.remove("key1"); + + Assertions.assertThat(cache.get("key1")).isNull(); + Assertions.assertThat(cache.get("key2")).isEqualTo("value2"); + Assertions.assertThat(cache.get("key3")).isEqualTo("value3"); + } + + @Test + public void testClear() + { + cache = new AXPCache(true); + cache.put("key1", "value1", 100); + cache.put("key2", "value2", 100); + cache.put("key3", "value3", 100); + + cache.clear(); + + Assertions.assertThat(cache.get("key1")).isNull(); + Assertions.assertThat(cache.get("key2")).isNull(); + Assertions.assertThat(cache.get("key3")).isNull(); + } + + @Test + public void testSize() + { + cache = new AXPCache(true); + cache.put("key1", "value1", 100); + cache.put("key2", "value2", 100); + cache.put("key3", "value3", 100); + + Assertions.assertThat(cache.size()).isEqualTo(3); + } +} \ No newline at end of file diff --git a/components/framework-axp/src/test/java/framework/cache/impl/DefaultAXPLoadingCacheTest.java b/components/framework-axp/src/test/java/framework/cache/impl/DefaultAXPLoadingCacheTest.java new file mode 100644 index 00000000..28d13529 --- /dev/null +++ b/components/framework-axp/src/test/java/framework/cache/impl/DefaultAXPLoadingCacheTest.java @@ -0,0 +1,264 @@ +package framework.cache.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.google.common.collect.Lists; +import framework.cache.AXPCache; +import framework.cache.IAXPCacheLoader; +import framework.cache.IAXPLoadingCache; +import org.fest.reflect.core.Reflection; +import org.mockito.Mockito; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyList; +import static org.mockito.Mockito.*; +import static org.testng.Assert.fail; + + +public class DefaultAXPLoadingCacheTest +{ + private TestCacheLoader mockCacheLoader; + + private DefaultAXPLoadingCache loadingCache; + + private AXPCache axpCache; + + @BeforeMethod + public void setUp() + { + mockCacheLoader = mock(TestCacheLoader.class); + + loadingCache = new DefaultAXPLoadingCache<>(mockCacheLoader, new AXPCache(true)); + + axpCache = Reflection.field("axpCache").ofType(AXPCache.class).in(this.loadingCache).get(); + axpCache.put(getTestCacheKey(1), "1*"); + axpCache.put(getTestCacheKey(2), "2"); + axpCache.put(getTestCacheKey(3), "3*"); + } + + @Test + public void testGetDataAsMap() + { + try + { + final List keyList = Lists.newArrayList(); + keyList.add(getTestCacheKey(1)); + keyList.add(getTestCacheKey(4)); + keyList.add(getTestCacheKey(3)); + keyList.add(getTestCacheKey(5)); + + Mockito.when(mockCacheLoader.load(anyList())).thenCallRealMethod(); + + Map dataMap = loadingCache.getDataAsMap(keyList); + + assertThat(dataMap).isNotNull(); + assertThat(dataMap.size()).isEqualTo(4); + assertThat(dataMap.get(getTestCacheKey(1))).isEqualTo( "1*"); + assertThat(dataMap.get(getTestCacheKey(4))).isEqualTo( "4"); + assertThat(dataMap.get(getTestCacheKey(3))).isEqualTo( "3*"); + assertThat(dataMap.get(getTestCacheKey(5))).isEqualTo( "5"); + + assertThat(axpCache.size()).isEqualTo( 5); + assertThat(axpCache.get(getTestCacheKey(1))).isEqualTo( "1*"); + assertThat(axpCache.get(getTestCacheKey(2))).isEqualTo( "2"); + assertThat(axpCache.get(getTestCacheKey(3))).isEqualTo( "3*"); + assertThat(axpCache.get(getTestCacheKey(4))).isEqualTo( "4"); + assertThat(axpCache.get(getTestCacheKey(5))).isEqualTo( "5"); + + verify(mockCacheLoader, only()).load(anyList()); + + dataMap = loadingCache.getDataAsMap(keyList); + + assertThat(dataMap).isNotNull(); + assertThat(dataMap.size()).isEqualTo( 4); + assertThat(dataMap.get(getTestCacheKey(1))).isEqualTo( "1*"); + assertThat(dataMap.get(getTestCacheKey(4))).isEqualTo( "4"); + assertThat(dataMap.get(getTestCacheKey(3))).isEqualTo( "3*"); + assertThat(dataMap.get(getTestCacheKey(5))).isEqualTo( "5"); + + assertThat(axpCache.size()).isEqualTo( 5); + assertThat(axpCache.get(getTestCacheKey(1))).isEqualTo("1*"); + assertThat(axpCache.get(getTestCacheKey(2))).isEqualTo("2"); + assertThat(axpCache.get(getTestCacheKey(3))).isEqualTo("3*"); + assertThat(axpCache.get(getTestCacheKey(4))).isEqualTo("4"); + assertThat(axpCache.get(getTestCacheKey(5))).isEqualTo("5"); + + verify(mockCacheLoader, only()).load(anyList()); + } + catch (Exception e) + { + e.printStackTrace(); + + fail(); + } + } + + @Test + public void testGetDataAsList() + { + try + { + final List keyList = Lists.newLinkedList(); + keyList.add(getTestCacheKey(1)); + keyList.add(getTestCacheKey(4)); + keyList.add(getTestCacheKey(3)); + keyList.add(getTestCacheKey(5)); + + when(mockCacheLoader.load(anyList())).thenCallRealMethod(); + + List dataList = loadingCache.getDataAsList(keyList); + + assertThat(dataList).isNotNull(); + assertThat(dataList.size()).isEqualTo( 4); + assertThat(dataList.get(0)).isEqualTo( "1*"); + assertThat(dataList.get(1)).isEqualTo( "4"); + assertThat(dataList.get(2)).isEqualTo( "3*"); + assertThat(dataList.get(3)).isEqualTo( "5"); + + assertThat(axpCache.size()).isEqualTo( 5); + assertThat(axpCache.get(getTestCacheKey(1))).isEqualTo( "1*"); + assertThat(axpCache.get(getTestCacheKey(2))).isEqualTo( "2"); + assertThat(axpCache.get(getTestCacheKey(3))).isEqualTo( "3*"); + assertThat(axpCache.get(getTestCacheKey(4))).isEqualTo( "4"); + assertThat(axpCache.get(getTestCacheKey(5))).isEqualTo( "5"); + + verify(mockCacheLoader, only()).load(anyList()); + + dataList = loadingCache.getDataAsList(keyList); + + assertThat(dataList).isNotNull(); + assertThat(dataList.size()).isEqualTo( 4); + assertThat(dataList.get(0)).isEqualTo( "1*"); + assertThat(dataList.get(1)).isEqualTo( "4"); + assertThat(dataList.get(2)).isEqualTo( "3*"); + assertThat(dataList.get(3)).isEqualTo( "5"); + + assertThat(axpCache.size()).isEqualTo( 5); + assertThat(axpCache.get(getTestCacheKey(1))).isEqualTo( "1*"); + assertThat(axpCache.get(getTestCacheKey(2))).isEqualTo( "2"); + assertThat(axpCache.get(getTestCacheKey(3))).isEqualTo( "3*"); + assertThat(axpCache.get(getTestCacheKey(4))).isEqualTo( "4"); + assertThat(axpCache.get(getTestCacheKey(5))).isEqualTo( "5"); + + verify(mockCacheLoader, only()).load(anyList()); + } + catch (Exception e) + { + e.printStackTrace(); + + fail(); + } + } + + @Test + public void testGetData_cached() throws Exception + { + TestCacheKey key = getTestCacheKey(1); + + String result = loadingCache.getData(key); + + assertThat(result).isEqualTo( "1*"); + } + + @Test + public void testGetData_nonCached() throws Exception + { + TestCacheKey key = getTestCacheKey(6); + List keys = Lists.newArrayList(key); + when(mockCacheLoader.load(keys)).thenCallRealMethod(); + + String result = loadingCache.getData(key); + + assertThat(result).isEqualTo("6"); + verify(mockCacheLoader, only()).load(keys); + } + + @Test + public void testUpdate() throws Exception + { + TestCacheKey key = getTestCacheKey(7); + List keys = Lists.newArrayList(key); + + loadingCache.update(key, "7"); + + assertThat(loadingCache.getData(key)).isEqualTo( "7"); + verify(mockCacheLoader, never()).load(keys); + } + + @Test + public void testClear() throws Exception + { + IAXPLoadingCache cache = + new DefaultAXPLoadingCache<>(mockCacheLoader, new AXPCache(true)); + TestCacheKey key = getTestCacheKey(8); + + loadingCache.clear(); + + assertThat(cache.getData(key)).isNull(); + } + + private TestCacheKey getTestCacheKey(int key) + { + return new TestCacheKey(key, String.valueOf(key)); + } + + private static class TestCacheKey + { + + private final int key; + + private final String anotherAttribute; + + TestCacheKey(int key, String anotherAttribute) + { + this.key = key; + this.anotherAttribute = anotherAttribute; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof TestCacheKey)) + { + return false; + } + + TestCacheKey that = (TestCacheKey) o; + + return key == that.key && Objects.equals(anotherAttribute, that.anotherAttribute); + } + + @Override + public int hashCode() + { + return Objects.hash(key, anotherAttribute); + } + } + + private class TestCacheLoader implements IAXPCacheLoader + { + + @Override + public Map load(List cacheKeyList) + { + final Map dataMap = new HashMap<>(); + + for (TestCacheKey testCacheKey : cacheKeyList) + { + dataMap.put(testCacheKey, testCacheKey.anotherAttribute); + } + + return dataMap; + } + } +} diff --git a/components/framework-axp/src/test/java/framework/configuration/ConfigFileReaderStrategyTest.java b/components/framework-axp/src/test/java/framework/configuration/ConfigFileReaderStrategyTest.java new file mode 100644 index 00000000..e6533789 --- /dev/null +++ b/components/framework-axp/src/test/java/framework/configuration/ConfigFileReaderStrategyTest.java @@ -0,0 +1,31 @@ +package framework.configuration; + +import framework.configuration.impl.JavaPropertyFileReadStrategy; +import framework.configuration.impl.XMLPropertyFileReadStrategy; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigFileReaderStrategyTest +{ + + @Test + public void testGetStrategy_extensionProperties() + { + assertThat(ConfigFileReaderStrategy.getInstance().getStrategy("properties")) + .isInstanceOf(JavaPropertyFileReadStrategy.class); + } + + @Test + public void testGetStrategy_extensionXml() + { + assertThat(ConfigFileReaderStrategy.getInstance().getStrategy("xml")) + .isInstanceOf(XMLPropertyFileReadStrategy.class); + } + + @Test(expectedExceptions = RuntimeException.class) + public void testGetStrategy_extensionOther() + { + ConfigFileReaderStrategy.getInstance().getStrategy("abc"); + } +} diff --git a/components/framework-axp/src/test/java/framework/configuration/impl/JavaPropertyFileReadStrategyTest.java b/components/framework-axp/src/test/java/framework/configuration/impl/JavaPropertyFileReadStrategyTest.java new file mode 100644 index 00000000..0a169d59 --- /dev/null +++ b/components/framework-axp/src/test/java/framework/configuration/impl/JavaPropertyFileReadStrategyTest.java @@ -0,0 +1,64 @@ +package framework.configuration.impl; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Map; + +import com.google.common.base.Joiner; +import framework.configuration.CarbonUtilsDelegator; +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.testng.Assert.fail; + +public class JavaPropertyFileReadStrategyTest +{ + + @Test + public void testReadFile() + { + try + { + CarbonUtilsDelegator mock = mock(CarbonUtilsDelegator.class); + + /** + * This is a dirty fix to resolve resource path. + * When building individual module and the parent, resolving two different resource locations. + */ + String currentPath = Paths.get("").toAbsolutePath().normalize().toString(); + if (currentPath.contains("components")) + + { + String path = Joiner + .on(File.separator) + .join("src", "test", "resources", "com", "wso2telco", "framework", "configuration", "impl", + "JavaPropertyFileReadStrategyTest"); + Mockito.when(mock.getCarbonConfigDirPath()).thenReturn(path); + } + else + { + String path = Joiner + .on(File.separator) + .join("components", "framework-axp", "src", "test", "resources", "com", "wso2telco", "framework", + "configuration", "impl", "JavaPropertyFileReadStrategyTest"); + Mockito.when(mock.getCarbonConfigDirPath()).thenReturn(path); + } + + JavaPropertyFileReadStrategy strategy = new JavaPropertyFileReadStrategy(mock); + Map propertiesMap = strategy.readFile("test.properties"); + + assertThat(propertiesMap).isNotEmpty(); + assertThat(propertiesMap).hasSize(17); + } + catch (IOException e) + { + e.printStackTrace(); + + fail(); + } + } + +} diff --git a/components/framework-axp/src/test/resources/com/wso2telco/framework/configuration/impl/JavaPropertyFileReadStrategyTest/test.properties b/components/framework-axp/src/test/resources/com/wso2telco/framework/configuration/impl/JavaPropertyFileReadStrategyTest/test.properties new file mode 100644 index 00000000..b1adfc91 --- /dev/null +++ b/components/framework-axp/src/test/resources/com/wso2telco/framework/configuration/impl/JavaPropertyFileReadStrategyTest/test.properties @@ -0,0 +1,25 @@ +sendSMSResourceURL=http://example.com/smsmessaging/v1/outbound/ +hubSubsGatewayEndpoint=https://gateway1a.mife.sla-mobile.com.my:8243/smsmessaging/v1/DeliveryInfoNotification +hubGateway=https://gateway1a.mife.sla-mobile.com.my:8243 +ussdGatewayEndpoint=https://gateway1a.mife.sla-mobile.com.my:8243/ussd/v1/inbound/ +#requestRouterUrl=http://IDEABIZAPPS/HTTPRequestRouter/route/HUB-USSD/?org= +hub_gateway_id=0001 +hub_gateway_provision_notify_url= + +hubMOSubsGatewayEndpoint=https://gateway1a.mife.sla-mobile.com.my:8243/smsmessaging/v1/ReceivedInfoNotification +hubDNSubsGatewayEndpoint=https://gateway1a.mife.sla-mobile.com.my:8243/smsmessaging/v1/DeliveryInfoNotification + +esbEndpoint=http://localhost:8281/payment + +#country code separated by (,) +search.oparatorOnHeaderMCC=94 +search.oparatorOnHeaderName=Operator +search.oparatorOnHeaderRegEx=(?<=XXX=)([a-zA-Z0-9])+ + +retry_on_fail=true +retry_count=3 +numberOfThreads=5 + +#If tokenpoolservice is set to 'true', tokenpoolResourceURL should point to the tokenpool endpoint (incl. trailing slash). +tokenpoolservice=false +tokenpoolResourceURL=http://localhost:8181/tokenservice/ \ No newline at end of file diff --git a/components/redis-client/pom.xml b/components/redis-client/pom.xml index 6ef6c0a2..cb930551 100644 --- a/components/redis-client/pom.xml +++ b/components/redis-client/pom.xml @@ -37,14 +37,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 1.7 - 1.7 - - org.apache.maven.plugins maven-shade-plugin diff --git a/components/sp-config-util/pom.xml b/components/sp-config-util/pom.xml index d06e459b..1bf3b29d 100644 --- a/components/sp-config-util/pom.xml +++ b/components/sp-config-util/pom.xml @@ -58,15 +58,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - 1.7 - 1.7 - - org.apache.felix maven-bundle-plugin diff --git a/pom.xml b/pom.xml index 170bcb40..1bf8a008 100644 --- a/pom.xml +++ b/pom.xml @@ -46,8 +46,36 @@ components/security/auth-filter components/security/user-profile features + components/framework-axp + + 1.5.8 + 2.17 + 1.2 + 3.3 + 4.11 + 4.4.3 + 2.1.1.wso2v1 + UTF-8 + 4.4.3 + 7.4.2.wso2v1 + 2.4.0.wso2v1 + 2.4.5-SNAPSHOT + 4.4.9 + 5.2.2 + 2.8.0 + 2.4.2 + 6.0.4 + 11.0.2 + + + 6.14.3 + 3.11.1 + 1.10.19 + 1.4.1 + + @@ -126,6 +154,37 @@ ${com.wso2telco.core.version} provided + + com.google.guava + guava + ${com.google.guava.version} + provided + + + + org.testng + testng + ${org.testng.version} + test + + + org.assertj + assertj-core + ${org.assertj.version} + test + + + org.mockito + mockito-all + ${org.mockito.version} + test + + + org.easytesting + fest-reflect + ${org.easytesting.version} + test + @@ -277,26 +336,6 @@ - - 1.5.8 - 2.17 - 1.2 - 3.3 - 4.11 - 4.4.3 - 2.1.1.wso2v1 - UTF-8 - 4.4.3 - 7.4.2.wso2v1 - 2.4.0.wso2v1 - ${project.version} - 4.4.9 - 5.2.2 - 2.8.0 - 2.4.2 - 6.0.4 - - notify @@ -328,7 +367,14 @@ - + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + +