Skip to content

Commit 51a32f0

Browse files
committed
Caffeine session store fix jooby-project#1765
1 parent 3af5ce9 commit 51a32f0

File tree

9 files changed

+275
-5
lines changed

9 files changed

+275
-5
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
== Caffeine
2+
3+
SessionStore implementation using https://github.com/ben-manes/caffeine[Caffeine] library.
4+
5+
=== Usage
6+
7+
1) Add the dependency:
8+
9+
[dependency, artifactId="jooby-caffeine"]
10+
.
11+
12+
2) Configure session store
13+
14+
.Java
15+
[source, java, role="primary"]
16+
----
17+
import io.jooby.caffeine.CaffeineSessionStore;
18+
19+
{
20+
setSessionStore(new CaffeineSessionStore()); <1>
21+
22+
get("/", ctx -> {
23+
Session session = ctx.session(); <2>
24+
// work with session
25+
});
26+
}
27+
----
28+
29+
.Kotlin
30+
[source, kt, role="secondary"]
31+
----
32+
import io.jooby.caffeine.CaffeineSessionStore
33+
34+
{
35+
sessionStore = CaffeineSessionStore() <1>
36+
37+
get("/") {
38+
val session = ctx.session() <2>
39+
// work with session
40+
}
41+
}
42+
----
43+
44+
<1> Configure session store
45+
<2> Use HTTP Session
46+
47+
Default time out is `30` minutes, to provide your own timeout:
48+
49+
new CaffeineSessionStore(Duration.ofHours(1))
50+
51+
For absolute control you can provide your own cache:
52+
53+
Cache cache = ...;
54+
new CaffeineSessionStore(Duration.ofHours(1))

docs/asciidoc/modules/modules.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ Available modules are listed next.
4848
* link:modules/pac4j[Pac4j]: Security engine module.
4949

5050
=== Session Store
51-
* link:modules/jwt-session-store[JWT Session Store]: JSON Web Token session store.
52-
* link:modules/redis#redis-http-session[Redis Session Store]: Save session data on redis.
51+
* link:modules/caffeine[Caffeine]: In-memory session store using Caffeine cache.
52+
* link:modules/jwt-session-store[JWT]: JSON Web Token session store.
53+
* link:modules/redis#redis-http-session[Redis]: Save session data on redis.
5354

5455
=== Scheduler
5556
* link:modules/quartz[Quartz]: Quartz scheduler module.

docs/asciidoc/session.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,5 +254,6 @@ Like with `memory` session store you can use HTTP headers:
254254

255255
In addition to built-in memory stores we do provide:
256256

257-
* link:modules/jwt-session-store[JWT Session Store]: JSON Web Token session store.
258-
* link:modules/redis#redis-http-session[Redis Session Store]: Redis session store.
257+
* link:modules/caffeine[Caffeine]: In-memory session store using Caffeine cache.
258+
* link:modules/jwt-session-store[JWT]: JSON Web Token session store.
259+
* link:modules/redis#redis-http-session[Redis]: Redis session store.

modules/jooby-bom/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<aws-java-sdk.version>1.11.791</aws-java-sdk.version>
2525
<boringssl.version>2.0.27.Final</boringssl.version>
2626
<bucket4j-core.version>4.10.0</bucket4j-core.version>
27+
<caffeine.version>2.8.4</caffeine.version>
2728
<checkstyle.version>8.32</checkstyle.version>
2829
<commons-io.version>2.7</commons-io.version>
2930
<compile-testing.version>0.18</compile-testing.version>
@@ -354,6 +355,18 @@
354355
<version>${jooby.version}</version>
355356
<type>jar</type>
356357
</dependency>
358+
<dependency>
359+
<groupId>io.jooby</groupId>
360+
<artifactId>jooby-caffeine</artifactId>
361+
<version>${jooby.version}</version>
362+
<type>jar</type>
363+
</dependency>
364+
<dependency>
365+
<groupId>com.github.ben-manes.caffeine</groupId>
366+
<artifactId>caffeine</artifactId>
367+
<version>${caffeine.version}</version>
368+
<type>jar</type>
369+
</dependency>
357370
<dependency>
358371
<groupId>org.ow2.asm</groupId>
359372
<artifactId>asm</artifactId>

modules/jooby-caffeine/pom.xml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5+
6+
<parent>
7+
<groupId>io.jooby</groupId>
8+
<artifactId>modules</artifactId>
9+
<version>2.8.5-SNAPSHOT</version>
10+
</parent>
11+
12+
<modelVersion>4.0.0</modelVersion>
13+
<artifactId>jooby-caffeine</artifactId>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>com.google.code.findbugs</groupId>
18+
<artifactId>jsr305</artifactId>
19+
<scope>provided</scope>
20+
</dependency>
21+
22+
<dependency>
23+
<groupId>io.jooby</groupId>
24+
<artifactId>jooby</artifactId>
25+
<version>${jooby.version}</version>
26+
</dependency>
27+
28+
<!-- Caffeine -->
29+
<dependency>
30+
<groupId>com.github.ben-manes.caffeine</groupId>
31+
<artifactId>caffeine</artifactId>
32+
<version>2.8.4</version>
33+
</dependency>
34+
35+
<!-- Test dependencies -->
36+
<dependency>
37+
<groupId>org.junit.jupiter</groupId>
38+
<artifactId>junit-jupiter-engine</artifactId>
39+
<scope>test</scope>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>org.mockito</groupId>
44+
<artifactId>mockito-core</artifactId>
45+
</dependency>
46+
47+
<dependency>
48+
<groupId>org.jacoco</groupId>
49+
<artifactId>org.jacoco.agent</artifactId>
50+
<classifier>runtime</classifier>
51+
<scope>test</scope>
52+
</dependency>
53+
</dependencies>
54+
</project>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.caffeine;
7+
8+
import com.github.benmanes.caffeine.cache.Cache;
9+
import com.github.benmanes.caffeine.cache.Caffeine;
10+
import io.jooby.Context;
11+
import io.jooby.Session;
12+
import io.jooby.SessionStore;
13+
import io.jooby.SessionToken;
14+
15+
import javax.annotation.Nonnull;
16+
import javax.annotation.Nullable;
17+
import java.time.Duration;
18+
import java.util.concurrent.ConcurrentHashMap;
19+
20+
/**
21+
* Caffeine session store.
22+
*
23+
* Usage:
24+
* <pre>{@code
25+
* {
26+
* setSessionStore(new CaffeineSessionStore());
27+
* }
28+
* }</pre>
29+
*
30+
* Default session timeout is: <code>30 minutes</code>.
31+
*
32+
* @author edgar
33+
* @since 2.8.5
34+
*/
35+
public class CaffeineSessionStore implements SessionStore {
36+
37+
private final Cache<String, Session> cache;
38+
39+
private SessionToken token = SessionToken.cookieId(SessionToken.SID);
40+
41+
/**
42+
* Creates a new session store using the given cache.
43+
*
44+
* @param cache Cache.
45+
*/
46+
public CaffeineSessionStore(@Nonnull Cache<String, Session> cache) {
47+
this.cache = cache;
48+
}
49+
50+
/**
51+
* Creates a new session store with given session timeout.
52+
*
53+
* @param timeout Session timeout.
54+
*/
55+
public CaffeineSessionStore(@Nonnull Duration timeout) {
56+
this.cache = Caffeine.newBuilder()
57+
.expireAfterAccess(timeout)
58+
.build();
59+
}
60+
61+
/**
62+
* Creates a new session store with timeout of <code>30 minutes</code>.
63+
*/
64+
public CaffeineSessionStore() {
65+
this(Duration.ofMinutes(DEFAULT_TIMEOUT));
66+
}
67+
68+
/**
69+
* Session token.
70+
*
71+
* @return Session token. Uses a cookie by default: {@link SessionToken#SID}.
72+
*/
73+
public @Nonnull SessionToken getToken() {
74+
return token;
75+
}
76+
77+
/**
78+
* Set custom session token.
79+
*
80+
* @param token Session token.
81+
* @return This store.
82+
*/
83+
public @Nonnull CaffeineSessionStore setToken(@Nonnull SessionToken token) {
84+
this.token = token;
85+
return this;
86+
}
87+
88+
@Nonnull @Override public Session newSession(@Nonnull Context ctx) {
89+
String sessionId = token.newToken();
90+
Session session = cache
91+
.get(sessionId, key -> Session.create(ctx, sessionId, new ConcurrentHashMap<>()));
92+
93+
token.saveToken(ctx, sessionId);
94+
95+
return session;
96+
}
97+
98+
@Nullable @Override public Session findSession(@Nonnull Context ctx) {
99+
String sessionId = token.findToken(ctx);
100+
if (sessionId == null) {
101+
return null;
102+
}
103+
Session session = cache.getIfPresent(sessionId);
104+
if (session != null) {
105+
token.saveToken(ctx, sessionId);
106+
}
107+
return session;
108+
}
109+
110+
@Override public void deleteSession(@Nonnull Context ctx, @Nonnull Session session) {
111+
cache.invalidate(session.getId());
112+
token.deleteToken(ctx, session.getId());
113+
}
114+
115+
@Override public void touchSession(@Nonnull Context ctx, @Nonnull Session session) {
116+
token.saveToken(ctx, session.getId());
117+
}
118+
119+
@Override public void saveSession(@Nonnull Context ctx, @Nonnull Session session) {
120+
// NOOP due we do everything on memory
121+
}
122+
123+
@Override public void renewSessionId(@Nonnull Context ctx, @Nonnull Session session) {
124+
125+
}
126+
}

modules/jooby-redis/src/main/java/io/jooby/redis/RedisSessionStore.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,24 @@ public RedisSessionStore(@Nonnull StatefulRedisConnection connection) {
158158
.ifPresent(seconds -> commands.expire(redisId, seconds));
159159
Instant lastAccessedTime = Instant.parse(data.remove(LAST_ACCESSED_AT));
160160
Instant createdAt = Instant.parse(data.remove(CREATED_AT));
161+
162+
token.saveToken(ctx, sessionId);
163+
161164
return Session.create(ctx, sessionId, new ConcurrentHashMap<>(data))
162165
.setCreationTime(createdAt)
163166
.setLastAccessedTime(lastAccessedTime);
164167
}
165168

166169
@Override public void deleteSession(@Nonnull Context ctx, @Nonnull Session session) {
167-
connection.async().del(key(session.getId()));
170+
String sessionId = session.getId();
171+
connection.async().del(key(sessionId));
172+
173+
token.deleteToken(ctx, sessionId);
168174
}
169175

170176
@Override public void touchSession(@Nonnull Context ctx, @Nonnull Session session) {
171177
saveSession(ctx, session);
178+
172179
token.saveToken(ctx, session.getId());
173180
}
174181

modules/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<module>jooby-flyway</module>
3434
<module>jooby-ebean</module>
3535
<module>jooby-redis</module>
36+
<module>jooby-caffeine</module>
3637

3738
<module>jooby-jackson</module>
3839
<module>jooby-gson</module>

pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<flyway.version>6.4.3</flyway.version>
3939
<graphql-java.version>15.0</graphql-java.version>
4040
<lettuce.version>5.3.0.RELEASE</lettuce.version>
41+
<caffeine.version>2.8.4</caffeine.version>
4142

4243
<config.version>1.4.0</config.version>
4344

@@ -441,6 +442,18 @@
441442
<version>${jooby.version}</version>
442443
</dependency>
443444

445+
<dependency>
446+
<groupId>io.jooby</groupId>
447+
<artifactId>jooby-caffeine</artifactId>
448+
<version>${jooby.version}</version>
449+
</dependency>
450+
451+
<dependency>
452+
<groupId>com.github.ben-manes.caffeine</groupId>
453+
<artifactId>caffeine</artifactId>
454+
<version>${caffeine.version}</version>
455+
</dependency>
456+
444457
<!-- ASM -->
445458
<dependency>
446459
<groupId>org.ow2.asm</groupId>

0 commit comments

Comments
 (0)