jsr107 / jsr107spec

JSR107 Cache Specification
Apache License 2.0
413 stars 164 forks source link

Caching is missing a cleanup method #381

Closed Donnerbart closed 2 years ago

Donnerbart commented 6 years ago

The class Caching is missing a cleanup method to ease the testing of JCache.

In our project we have multiple CachingProvider implementations (e.g. a server and client implementation). If we run the server and client tests in the same JVM, the CachingProvider from the first invoked test remains in the internal CachingProviderRegistry. This crashes the following tests, since we either get the wrong CachingProvider via Caching.getCachingProvider() or we run into this CacheException:

Multiple CachingProviders have been configured when only a single CachingProvider is expected

Closing the CachingProvider doesn't help, since the spec defines that it will be re-used, not removed.

A nice way to solve this would be a Caching.clear() or Caching.destroy() method. At the moment we are solving this issue with this nasty helper method:

/**
 * Closes and removes the {@link javax.cache.spi.CachingProvider}
 * from the static registry in {@link Caching}.
 */
public static void clearCachingProviderRegistry() {
    try {
        for (CachingProvider cachingProvider : Caching.getCachingProviders()) {
            cachingProvider.close();
        }

        // retrieve the CachingProviderRegistry instance
        Field providerRegistryField = Caching.class.getDeclaredField("CACHING_PROVIDERS");
        providerRegistryField.setAccessible(true);

        Class<?> providerRegistryClass = providerRegistryField.getType();
        Object providerRegistryInstance = providerRegistryField.get(Caching.class);

        // retrieve the map with the CachingProvider instances
        Field providerMapField = providerRegistryClass.getDeclaredField("cachingProviders");
        providerMapField.setAccessible(true);

        Class<?> providerMapClass = providerMapField.getType();
        Object providerMap = providerMapField.get(providerRegistryInstance);

        // clear the map
        Method clearMethod = providerMapClass.getDeclaredMethod("clear");
        clearMethod.invoke(providerMap);

        // retrieve the ClassLoader of the CachingProviderRegistry
        Field classLoaderField = providerRegistryClass.getDeclaredField("classLoader");
        classLoaderField.setAccessible(true);

        // set the ClassLoader to null
        classLoaderField.set(providerRegistryInstance, null);
    } catch (Exception e) {
        e.printStackTrace();
        fail("Could not cleanup CachingProvider registry: " + e.getMessage());
    }
}
cruftex commented 6 years ago

Based on the current standard the current implementations have the guarantee that the CachingProvider is only created once during JVM lifetime. Adding a Caching.clear() as you suggest, would break that assumption. That may or may not work across all implementations. Besides, testing this behavior in the TCK would be very difficult.

During a normal application run, there is no use in redefining the default caching provider midway. If your tests need to run with different providers, its best to specify them explicitly.

Opting for closing this issue.

gregrluck commented 6 years ago

API Change so 2.0