Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Fix #2 Better configuration #3

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 2 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ dependencies {
console "org.grails:grails-console"
profile "org.grails.profiles:web-plugin"
provided "org.grails:grails-plugin-services"
provided "org.grails:grails-plugin-domain-class"

testCompile "org.grails:grails-datastore-gorm-test"
testCompile "org.grails:grails-plugin-testing"
testCompile "org.grails:grails-web-testing-support"
testCompile "org.grails.plugins:geb"
Expand All @@ -45,8 +43,7 @@ dependencies {

compile "org.grails.plugins:cache:4.0.0"

compile 'com.google.guava:guava:25.0-jre'

compile "com.google.guava:guava:30.1.1-jre"
}

bootRun {
Expand All @@ -65,6 +62,6 @@ grailsPublish {
}
title = project.name
desc = "The guava cache provides a simple in memory cache with maximal capacity and TTL."
developers = [mkobel:"Moritz obel"]
developers = [mkobel:"Moritz obel", sbglasius: 'Søren Berg Glasius']
}

3 changes: 1 addition & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
grailsVersion=3.3.5
gormVersion=6.1.9.RELEASE
grailsVersion=3.3.12
gradleWrapperVersion=3.5
11 changes: 9 additions & 2 deletions grails-app/conf/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,15 @@ endpoints:
grails:
cache:
guava:
defaultTtl: 3600
defaultTtl: 300
defaultMaxCapacity: 2000
caches:
demo:
maxCapacity: 10
ttl: 10
ttl: 10
cache1:
ttl: 10
cache2:
maxCapacity: 20
cache3:
allowNullValues: false
36 changes: 36 additions & 0 deletions src/integration-test/groovy/com/demo/CacheGuavaPluginSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.demo

import grails.cache.guava.GrailsGuavaCache
import grails.testing.mixin.integration.Integration
import org.grails.plugin.cache.GrailsCacheManager
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
import spock.lang.Unroll

@Integration
class CacheGuavaPluginSpec extends Specification {

@Autowired
GrailsCacheManager grailsCacheManager

@Unroll('config for #name cache')
void "config settings for various caches and not-defined cache"() {
when:
GrailsGuavaCache cache = grailsCacheManager.getCache(name) as GrailsGuavaCache

then:
with(cache) {
ttl == expectedTtl
capacity == expectedCapacity
allowNullValues == expectedAllowNullValues
}

where:
name | expectedTtl | expectedCapacity | expectedAllowNullValues
'cache1' | 10 | 2000 | true
'cache2' | 300 | 20 | true
'cache3' | 300 | 2000 | false
'not-defined' | 300 | 2000 | true
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(value = 'grails.cache.guava', ignoreInvalidFields = true)
class CacheGuavaPluginConfiguration {

Boolean clearAtStartup = false
Map<String, CacheGruavaConfig> caches = [:]
Map<String, CacheGuavaConfig> caches = [:]
Integer defaultTtl
Integer defaultMaxCapacity
Boolean defaultAllowNullValues

static class CacheGruavaConfig {
static class CacheGuavaConfig {
Integer maxCapacity
Integer ttl
Boolean allowNullValues
}
}
}
170 changes: 170 additions & 0 deletions src/main/groovy/grails/cache/guava/GrailsGuavaCache.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/* Copyright 2012-2013 SpringSource.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package grails.cache.guava

import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import grails.plugin.cache.GrailsCache
import grails.plugin.cache.GrailsValueWrapper
import groovy.transform.CompileStatic
import org.springframework.cache.support.SimpleValueWrapper

import java.util.concurrent.Callable
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit

/**
* @author Moritz Kobel
* based on Jakob Drangmeister GrailsConcurrentLinkedMapCache
*/
@CompileStatic
class GrailsGuavaCache implements GrailsCache {

private static final Object NULL_HOLDER = new NullHolder()
private String name
private int capacity
private int ttl
private final Cache<Object, Object> store
private final boolean allowNullValues

/**
* Create a new Guava Cache with the specified name
* and capacity
* @param name the name of the cache
* @param capacity of the cache
* @param ttl of the cache
* @param allowNullValues null values (default is true)
*/
GrailsGuavaCache(String name, int capacity, int ttl, boolean allowNullValues = true) {
this.name = name
this.capacity = capacity
this.ttl = ttl
this.allowNullValues = allowNullValues
this.store = CacheBuilder.newBuilder()
.maximumSize(capacity)
.expireAfterWrite(ttl, TimeUnit.SECONDS)
.build()
}

@Override
final String getName() {
return this.name
}

@Override
final Cache<Object, Object> getNativeCache() {
return this.store
}

@Override
GrailsValueWrapper get(Object key) {
Object value = getNativeCache().getIfPresent(key)
return value == null ? null : new GrailsValueWrapper(fromStoreValue(value), null)
}

@Override
<T> T get(Object key, Class<T> type) {
Object value = getNativeCache().getIfPresent(key)
if (value != null && type != null && !type.isInstance(value)) {
throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value)
}
return (T) value
}

@Override
<T> T get(Object key, Callable<T> valueLoader) {
try {
return (T)this.store.get(key, valueLoader)
} catch (ExecutionException ignore) {
return null
}
}


@Override
void put(Object key, Object value) {
this.store.put(key, toStoreValue(value))
}

@Override
ValueWrapper putIfAbsent(Object key, Object value) {
Object existing = get(key)
if ( existing == null ) {
put(key,value)
existing = get(key)
}
return toWrapper(existing)
}


@Override
void evict(Object key) {
this.store.invalidate(key)
}

@Override
void clear() {
this.store.invalidateAll()
}

/**
* Convert the given value from the internal store to a user value
* returned from the get method (adapting {@code null}).
* @param storeValue the store value
* @return the value to return to the user
*/
protected Object fromStoreValue(Object storeValue) {
if (this.allowNullValues && storeValue == NULL_HOLDER) {
return null
}
return storeValue
}

/**
* Convert the given user value, as passed into the put method,
* to a value in the internal store (adapting {@code null}).
* @param userValue the given user value
* @return the value to store
*/
protected Object toStoreValue(Object userValue) {
if (this.allowNullValues && userValue == null) {
return NULL_HOLDER
}
return userValue
}

private ValueWrapper toWrapper(Object value) {
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null)
}

@Override
Collection<Object> getAllKeys() {
return this.store.asMap().keySet()
}

int getCapacity() {
return capacity
}

boolean isAllowNullValues() {
return allowNullValues
}

long getSize() {
return this.store.size()
}

private static class NullHolder implements Serializable { }
}
Loading