granadaCoder
granadaCoder

Reputation: 27862

cache2k and Generic T do not play nice together

In the cache2k User Guide, §2.3. Cache Aside has a code sample and sentence about cache-aside.

Cache<String, String> routeToAirline = new Cache2kBuilder<String, String>() {}
  .name("routeToAirline")
  .build();

private String findFavoriteAirline(String origin, String destination) {
  // expensive operation to find the best airline for this route
  // for example, ask all friends...
}

public String lookupFavoirteAirline(String origin, String destination) {
  String route = origin + "-" + destination;
  String airline = routeToAirline.peek(route);
  if (airline == null) {
    airline = findFavoriteAirline(origin, destination);
    routeToAirline.put(route, airline);
  }
  return airline;
}

The above pattern is called cache aside.

And I'm like "Great....I love Cache Aside pattern". So I try to implement one using generics instead of hard coding the (value) type.

I came up with this using generics. And I inject the method that "gets" the item (Supplier). Totally reusable code!

package mypackage;

import java.util.function.Supplier;

import org.cache2k.Cache;
import org.cache2k.Cache2kBuilder;

//import mypackage.interfaces.IGenericCacheAside;

public class Cache2kGenericCacheAside<TEntity> ////implements IGenericCacheAside<TEntity> {

    public final String CacheKeyPrefix = "GenericCacheAsidePrefix";

    private volatile Cache<String, TEntity> theCache = null; /* static not allowed for TEntity */

    public TEntity GetCacheAsideItem(String uniqueIdentifier, long itemLifeMiliseconds,
            final Supplier<TEntity> valueFactory) {
        this.initiateCacheObject();
        String cacheKey = this.GetFullCacheKey(uniqueIdentifier);
        TEntity cachedOrFreshItem = this.GetFromCache(cacheKey, itemLifeMiliseconds, valueFactory);
        return cachedOrFreshItem;
    }

    public TEntity RemoveCacheAsideItem(String uniqueIdentifier) {
        TEntity removedItem = null;
        String cacheKey = this.GetFullCacheKey(uniqueIdentifier);
        if (this.theCache.containsKey(uniqueIdentifier)) {
            removedItem = this.theCache.peekAndRemove(cacheKey);
        }
        return removedItem;
    }

    private void initiateCacheObject(/* long duration, TimeUnit tu, long capacity */) {
        if (null == this.theCache) {

            theCache = new Cache2kBuilder<String, TEntity>() {
            }.name("myCache").eternal(true).build();

        }
    }



    private TEntity GetFromCache(String cacheKey, long millis, final Supplier<TEntity> valueFactory) {
        TEntity cachedOrFreshItem = theCache.peek(cacheKey);
        if (cachedOrFreshItem == null) {
            cachedOrFreshItem = valueFactory.get();
            theCache.put(cacheKey, cachedOrFreshItem);
            theCache.expireAt(cacheKey, millis);
        }

        return cachedOrFreshItem;
    }

    private String GetFullCacheKey(String uniqueIdentifier) {
        String returnValue = CacheKeyPrefix + uniqueIdentifier;
        return returnValue;
    }
}

I get a runtime error:

java.lang.IllegalArgumentException: The run time type is not available, got: TEntity

I think I may have stumbled into the this previously unknown world of type erasure.

Is there any way to implement this Generic-CacheAside code? This hidden-gem (erasure) is horrible.

<dependency>
    <groupId>org.cache2k</groupId>
    <artifactId>cache2k-api</artifactId>
    <version>1.2.0.Final</version>
</dependency>

APPEND:

Here is an example. Basically, anytime I need to cache a "Something" that isn't driven by a parameter for retrieval. In my example below, I am caching SystemSetting(s). There is no parameter to drive the retrieval.

private static int NewedUpCounter = 0;
private static int CurrentRunCacheReads = 0;

private static void RunCacheAsideStuff() {

    /* example ONLY. use construction-injection for "real" code */
    ////IGenericCacheAside<Collection<SystemSetting>> igca = new Cache2kGenericCacheAside<Collection<SystemSetting>>();
    /* or */
    Cache2kGenericCacheAside<Collection<SystemSetting>> igca = new Cache2kGenericCacheAside<Collection<SystemSetting>>();

    for (int i = 0; i < 20; i++) {

        /* in the below, it shows how the "time to keep in the cache" might change over time */
        int cacheMilliseconds = 2500 + (500 * i);
        System.out.println(String.format("        cacheMilliseconds=%1s",
                cacheMilliseconds));            
        Collection<SystemSetting> cacheAsideSettings = igca.GetCacheAsideItem("myuniqueIdentifier", cacheMilliseconds,
                TimeUnit.MILLISECONDS, App::CreateDummySystemSettings);

        if (null != cacheAsideSettings) {
            System.out.println("--------------");
            System.out.println(String.format("        CurrentRunCacheReads=%1s",
                    ++CurrentRunCacheReads));                   
            System.out.println(String.format("        Cached Collection<SystemSetting> Read .. size=%1s",
                    cacheAsideSettings.size()));
            for (SystemSetting sett : cacheAsideSettings) {
                System.out.println(String.format(
                        "cacheAsideSettings !! SystemSetting.Key=%1s, SystemSetting.Value = %2s, i = %3s , Time= %4s",
                        sett.getSystemSettingKey(), sett.getSettingValue(), i, LocalDateTime.now()));
            }
            System.out.println("--------------");
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    System.out.println(String.format("NewedUpCounter=%1s",
            NewedUpCounter));

}

private static Collection<SystemSetting> CreateDummySystemSettings() {

    NewedUpCounter++;
    CurrentRunCacheReads=0;

    long LOWER_RANGE = 10000; // assign lower range value
    long UPPER_RANGE = 20000; // assign upper range value
    Random random = new Random();

    long randomValue = LOWER_RANGE + (long) (random.nextDouble() * (UPPER_RANGE - LOWER_RANGE));

    Collection<SystemSetting> returnItems = new ArrayList<>();
    for (int i = 101; i < 104; i++) {
        SystemSetting newSetting = new SystemSetting();
        newSetting.setSystemSettingKey(i);
        newSetting.setSettingValue(String.format("ValueOf%1s*", randomValue));
        returnItems.add(newSetting);
    }

    System.out.println(String.format("NEW Collection<SystemSetting> CREATED !! size=%1s  ************************************************", returnItems.size()));

    return returnItems;
}

And output. A sample that shows the item/time-in-cache can change.

        cacheMilliseconds=2500
NEW Collection<SystemSetting> CREATED !! size=3  ************************************************
--------------
        CurrentRunCacheReads=1
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14000*, i =   0 , Time= 2018-09-28T13:04:14.043
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14000*, i =   0 , Time= 2018-09-28T13:04:14.052
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14000*, i =   0 , Time= 2018-09-28T13:04:14.052
--------------
        cacheMilliseconds=3000
--------------
        CurrentRunCacheReads=2
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14000*, i =   1 , Time= 2018-09-28T13:04:15.052
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14000*, i =   1 , Time= 2018-09-28T13:04:15.053
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14000*, i =   1 , Time= 2018-09-28T13:04:15.053
--------------
        cacheMilliseconds=3500
--------------
        CurrentRunCacheReads=3
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14000*, i =   2 , Time= 2018-09-28T13:04:16.054
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14000*, i =   2 , Time= 2018-09-28T13:04:16.054
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14000*, i =   2 , Time= 2018-09-28T13:04:16.054
--------------
        cacheMilliseconds=4000
NEW Collection<SystemSetting> CREATED !! size=3  ************************************************
--------------
        CurrentRunCacheReads=1
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17155*, i =   3 , Time= 2018-09-28T13:04:17.055
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17155*, i =   3 , Time= 2018-09-28T13:04:17.055
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17155*, i =   3 , Time= 2018-09-28T13:04:17.055
--------------
        cacheMilliseconds=4500
--------------
        CurrentRunCacheReads=2
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17155*, i =   4 , Time= 2018-09-28T13:04:18.056
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17155*, i =   4 , Time= 2018-09-28T13:04:18.056
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17155*, i =   4 , Time= 2018-09-28T13:04:18.056
--------------
        cacheMilliseconds=5000
--------------
        CurrentRunCacheReads=3
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17155*, i =   5 , Time= 2018-09-28T13:04:19.057
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17155*, i =   5 , Time= 2018-09-28T13:04:19.058
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17155*, i =   5 , Time= 2018-09-28T13:04:19.058
--------------
        cacheMilliseconds=5500
--------------
        CurrentRunCacheReads=4
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17155*, i =   6 , Time= 2018-09-28T13:04:20.058
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17155*, i =   6 , Time= 2018-09-28T13:04:20.058
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17155*, i =   6 , Time= 2018-09-28T13:04:20.058
--------------
        cacheMilliseconds=6000
NEW Collection<SystemSetting> CREATED !! size=3  ************************************************
--------------
        CurrentRunCacheReads=1
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17444*, i =   7 , Time= 2018-09-28T13:04:21.059
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17444*, i =   7 , Time= 2018-09-28T13:04:21.060
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17444*, i =   7 , Time= 2018-09-28T13:04:21.060
--------------
        cacheMilliseconds=6500
--------------
        CurrentRunCacheReads=2
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17444*, i =   8 , Time= 2018-09-28T13:04:22.060
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17444*, i =   8 , Time= 2018-09-28T13:04:22.060
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17444*, i =   8 , Time= 2018-09-28T13:04:22.060
--------------
        cacheMilliseconds=7000
--------------
        CurrentRunCacheReads=3
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17444*, i =   9 , Time= 2018-09-28T13:04:23.060
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17444*, i =   9 , Time= 2018-09-28T13:04:23.060
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17444*, i =   9 , Time= 2018-09-28T13:04:23.060
--------------
        cacheMilliseconds=7500
--------------
        CurrentRunCacheReads=4
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17444*, i =  10 , Time= 2018-09-28T13:04:24.060
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17444*, i =  10 , Time= 2018-09-28T13:04:24.061
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17444*, i =  10 , Time= 2018-09-28T13:04:24.061
--------------
        cacheMilliseconds=8000
--------------
        CurrentRunCacheReads=5
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17444*, i =  11 , Time= 2018-09-28T13:04:25.061
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17444*, i =  11 , Time= 2018-09-28T13:04:25.061
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17444*, i =  11 , Time= 2018-09-28T13:04:25.061
--------------
        cacheMilliseconds=8500
--------------
        CurrentRunCacheReads=6
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf17444*, i =  12 , Time= 2018-09-28T13:04:26.063
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf17444*, i =  12 , Time= 2018-09-28T13:04:26.063
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf17444*, i =  12 , Time= 2018-09-28T13:04:26.064
--------------
        cacheMilliseconds=9000
NEW Collection<SystemSetting> CREATED !! size=3  ************************************************
--------------
        CurrentRunCacheReads=1
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  13 , Time= 2018-09-28T13:04:27.065
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  13 , Time= 2018-09-28T13:04:27.065
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  13 , Time= 2018-09-28T13:04:27.066
--------------
        cacheMilliseconds=9500
--------------
        CurrentRunCacheReads=2
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  14 , Time= 2018-09-28T13:04:28.066
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  14 , Time= 2018-09-28T13:04:28.066
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  14 , Time= 2018-09-28T13:04:28.066
--------------
        cacheMilliseconds=10000
--------------
        CurrentRunCacheReads=3
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  15 , Time= 2018-09-28T13:04:29.067
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  15 , Time= 2018-09-28T13:04:29.067
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  15 , Time= 2018-09-28T13:04:29.067
--------------
        cacheMilliseconds=10500
--------------
        CurrentRunCacheReads=4
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  16 , Time= 2018-09-28T13:04:30.068
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  16 , Time= 2018-09-28T13:04:30.068
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  16 , Time= 2018-09-28T13:04:30.068
--------------
        cacheMilliseconds=11000
--------------
        CurrentRunCacheReads=5
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  17 , Time= 2018-09-28T13:04:31.068
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  17 , Time= 2018-09-28T13:04:31.068
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  17 , Time= 2018-09-28T13:04:31.068
--------------
        cacheMilliseconds=11500
--------------
        CurrentRunCacheReads=6
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  18 , Time= 2018-09-28T13:04:32.068
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  18 , Time= 2018-09-28T13:04:32.068
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  18 , Time= 2018-09-28T13:04:32.068
--------------
        cacheMilliseconds=12000
--------------
        CurrentRunCacheReads=7
        Cached Collection<SystemSetting> Read .. size=3
cacheAsideSettings !! SystemSetting.Key=101, SystemSetting.Value = ValueOf14680*, i =  19 , Time= 2018-09-28T13:04:33.069
cacheAsideSettings !! SystemSetting.Key=102, SystemSetting.Value = ValueOf14680*, i =  19 , Time= 2018-09-28T13:04:33.069
cacheAsideSettings !! SystemSetting.Key=103, SystemSetting.Value = ValueOf14680*, i =  19 , Time= 2018-09-28T13:04:33.069
--------------
NewedUpCounter=4 /* this last one not accurate since its "bailing out */

Upvotes: 2

Views: 821

Answers (1)

newacct
newacct

Reputation: 122449

The anonymous class creation expression new Cache2kBuilder<String, String>() {} is used as a "super type token" to represent a parameterized type at runtime. The way they do this is you create a subclass (typically anonymous class) which extends a parameterized type, where the type arguments are concrete types fixed (hard-coded) at compile time. Since the superclass of the class is part of the class's declaration, it is stored with the generic information in the declaration part of the class file, and this information can be retrieved at runtime via reflection.

Note that what can be retrieved from the class file at runtime is exactly what was hard-coded at compile time. This is why new Cache2kBuilder<String, TEntity>() {} doesn't work -- what was hard-coded in the source code at compile-time is that there is a type variable called TEntity, instead of a concrete class.

Cache2k provides a different way to construct a Cache2kBuilder if the class cannot be fixed at compile-time, and will only be known at runtime:

theCache = Cache2kBuilder.of(String.class, entityClass)
  .name("myCache").eternal(true).build();

where entityClass is a Class<TEntity> which is the class object of the entity class at runtime. So you will have to also pass in the Class object for the entity, not just a Supplier for it.

Upvotes: 4

Related Questions