public interface SessionStore extends Service
Ratpack's session support cooperates with the implementation of this type found in the context registry.
The SessionModule
provides a default implementation that stores the data in local memory.
In order to persist session data in the store of your choice, simply override the binding for this type with your own implementation.
The store methods return Promise
and Operation
in order to support non blocking IO.
The store should not make any attempt to interpret the bytes that it is storing/loading.
Here is an example implementation that uses files on the filesystem to store session data.
import com.google.common.io.Files;
import com.google.inject.Singleton;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.util.AsciiString;
import ratpack.exec.Operation;
import ratpack.exec.Promise;
import ratpack.exec.Blocking;
import ratpack.guice.ConfigurableModule;
import ratpack.guice.Guice;
import ratpack.session.Session;
import ratpack.session.SessionModule;
import ratpack.session.SessionStore;
import ratpack.test.embed.EphemeralBaseDir;
import ratpack.test.embed.EmbeddedApp;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
public class Example {
static class FileSessionStore implements SessionStore {
private final ByteBufAllocator bufferAllocator;
private final File dir;
@Inject
public FileSessionStore(ByteBufAllocator bufferAllocator, FileSessionModule.Config config) {
this.bufferAllocator = bufferAllocator;
this.dir = config.dir;
}
@Override
public void onStart(@SuppressWarnings("deprecation") ratpack.server.StartEvent event) throws Exception {
Blocking.op(() -> {
assert dir.mkdirs() || dir.exists();
}).then();
}
@Override
public void onStop(@SuppressWarnings("deprecation") ratpack.server.StopEvent event) throws Exception {
Blocking.op(() -> {
Arrays.asList(dir.listFiles()).forEach(File::delete);
dir.delete();
}).then();
}
@Override
public Operation store(AsciiString sessionId, ByteBuf sessionData) {
return Blocking.op(() ->
Files.asByteSink(file(sessionId)).writeFrom(new ByteBufInputStream(sessionData))
);
}
@Override
public Promise<ByteBuf> load(AsciiString sessionId) {
File sessionFile = file(sessionId);
return Blocking.get(() -> {
if (sessionFile.exists()) {
ByteBuf buffer = bufferAllocator.buffer((int) sessionFile.length());
try {
Files.asByteSource(sessionFile).copyTo(new ByteBufOutputStream(buffer));
return buffer;
} catch (IOException e) {
buffer.release();
throw e;
}
} else {
return bufferAllocator.buffer(0, 0);
}
});
}
private File file(AsciiString sessionId) {
return new File(dir, sessionId.toString());
}
@Override
public Operation remove(AsciiString sessionId) {
return Blocking.op(() -> file(sessionId).delete());
}
@Override
public Promise<Long> size() {
return Blocking.get(() -> (long) dir.listFiles(File::isFile).length);
}
}
public static class FileSessionModule extends ConfigurableModule<FileSessionModule.Config> {
public static class Config {
File dir;
}
@Override
protected void configure() {
bind(SessionStore.class).to(FileSessionStore.class).in(Singleton.class);
}
}
public static void main(String... args) throws Exception {
EphemeralBaseDir.tmpDir().use(baseDir -> {
EmbeddedApp.of(s -> s
.registry(Guice.registry(b -> b
.module(SessionModule.class)
.module(FileSessionModule.class, c -> c.dir = baseDir.getRoot().toFile())
))
.handlers(c -> c
.get("set/:name/:value", ctx ->
ctx.get(Session.class).getData().then(sessionData -> {
sessionData.set(ctx.getPathTokens().get("name"), ctx.getPathTokens().get("value"));
ctx.render("ok");
})
)
.get("get/:name", ctx ->
ctx.render(ctx.get(Session.class).getData().map(sessionData -> sessionData.require(ctx.getPathTokens().get("name"))))
)
)
).test(httpClient -> {
assertEquals("ok", httpClient.getText("set/foo/bar"));
assertEquals("bar", httpClient.getText("get/foo"));
});
});
}
}
SessionModule
Modifier and Type | Method and Description |
---|---|
Promise<io.netty.buffer.ByteBuf> |
load(io.netty.util.AsciiString sessionId)
Reads the session data for the given id.
|
Operation |
remove(io.netty.util.AsciiString sessionId)
Removes the session data for the given id.
|
Promise<java.lang.Long> |
size()
The current number of sessions.
|
Operation |
store(io.netty.util.AsciiString sessionId,
io.netty.buffer.ByteBuf sessionData)
Writes the session data for the given id.
|
Operation store(io.netty.util.AsciiString sessionId, io.netty.buffer.ByteBuf sessionData)
The given byte buffer will not be modified by the caller, and will be released by the caller after the returned operation has completed (with error or without).
sessionId
- the identifier for the sessionsessionData
- the session dataPromise<io.netty.buffer.ByteBuf> load(io.netty.util.AsciiString sessionId)
The caller will release the promised byte buffer.
sessionId
- the identifier for the sessionOperation remove(io.netty.util.AsciiString sessionId)
sessionId
- the session idPromise<java.lang.Long> size()
The exact meaning of this value is implementation dependent.
-1
may be returned if the store does not support getting the size.