Interface SessionStore

  • All Superinterfaces:
    Service

    public interface SessionStore
    extends Service
    A persistent store of session data.

    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.

    Example implementation

    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.core.service.StartEvent;
     import ratpack.core.service.StopEvent;
     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.jupiter.api.Assertions.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(StartEvent event) throws Exception {
           Blocking.op(() -> {
             assert dir.mkdirs() || dir.exists();
           }).then();
         }
    
         @Override
         public void onStop(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"));
           });
         });
       }
     }
     
    See Also:
    SessionModule
    • Method Summary

      All Methods Instance Methods Abstract Methods 
      Modifier and Type Method 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<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.
    • Method Detail

      • store

        Operation store​(io.netty.util.AsciiString sessionId,
                        io.netty.buffer.ByteBuf sessionData)
        Writes the session data for the given id.

        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).

        Parameters:
        sessionId - the identifier for the session
        sessionData - the session data
        Returns:
        the store operation
      • load

        Promise<io.netty.buffer.ByteBuf> load​(io.netty.util.AsciiString sessionId)
        Reads the session data for the given id.

        The caller will release the promised byte buffer.

        Parameters:
        sessionId - the identifier for the session
        Returns:
        a promise for the session data
      • remove

        Operation remove​(io.netty.util.AsciiString sessionId)
        Removes the session data for the given id.
        Parameters:
        sessionId - the session id
        Returns:
        the remove operation
      • size

        Promise<Long> size()
        The current number of sessions.

        The exact meaning of this value is implementation dependent. -1 may be returned if the store does not support getting the size.

        Returns:
        a promise for the store size