containerCallScope
(either a HttpServletRequest
or a websocket endpoint event), websocketConnectionScope
(javax.websocket.Session
) and httpSessionScope
.
Copyright 2021 Piotr Morgwai Kotarbinski, Licensed under the Apache License, Version 2.0
latest release: 16.0
javax flavor
(javadoc) - supports Servlet 4.0.1
and Websocket 1.1
APIs
jakarta flavor
(javadoc) - supports Servlet 5.0.0
to at least 6.0.0
and Websocket 2.0.0
to at least 2.1.1
APIs
Provides the below Guice scopes:
Scopes bindings to either an HttpServletRequest
or a websocket event (connection opened/closed, message received, error occurred).
Spans over a single container-initiated call to either one of servlet's doXXX(...)
methods or to a websocket endpoint life-cycle method (annotated with one of the websocket annotations or overriding those of javax.websocket.Endpoint
or of registered javax.websocket.MessageHandler
s).
Having a common scope for servlet requests and websocket events allows to inject scoped objects both in servlets and endpoints without a need for 2 separate bindings in user Module
s.
Scopes bindings to a websocket connection (javax.websocket.Session
).
Spans over a lifetime of a given endpoint instance: all calls to life-cycle methods of a given endpoint instance (annotated with @OnOpen
, @OnMessage
, @OnError
, @OnClose
, or overriding those of javax.websocket.Endpoint
together with methods of registered MessageHandler
s) are executed within the same associated websocketConnectionScope
.
Scopes bindings to a given HttpSession
. Available both to servlets and websocket endpoints.
All the above scopes are built using guice-context-scopes lib, so they are automatically transferred to a new thread when dispatching using AsyncContext.dispatch()
or ServletContextTrackingExecutor
(see below).
Contains the above Scope
s, related ContextTracker
s and some helper methods.
Websocket ServerEndpoint
Configurator
that ensures that Endpoint
instances have their dependencies injected and that their methods run within websocket contexts, so that the above Scope
s work properly.
Base class for app ServletContextListener
s. Creates and configures an app-wide Guice Injector
instance, the above mentioned ServletModule
and performs bookkeeping related to GuiceServerEndpointConfigurator
and ServletContextTrackingExecutor
s. Also provides helper methods for creating and configuring programmatic Servlet
s, Filter
s and Endpoint
s.
Subclass of GuiceServerEndpointConfigurator
that additionally automatically registers and deregisters created Endpoint
instances to its associated WebsocketPingerService.
Subclass of GuiceServletContextListener
that uses PingingEndpointConfigurator
for programmatic Endpoint
s and configures app's WebsocketPingerService
.
A ThreadPoolExecutor
that upon dispatching a task, automatically transfers all the active Context
s to the thread running the task.
Binds tasks and callbacks (Runnable
s, Consumer
s, BiConsumer
s, Function
s and BiFunction
s) to contexts that were active at the time of binding. This can be used to transfer Context
s almost fully automatically when it's not possible to use GrpcContextTrackingExecutor
when switching threads (for example when providing callbacks as arguments to async functions). See a usage sample below.
@WebListener
public class ServletContextListener extends GuiceServletContextListener {
// ...or `extends PingingServletContextListener {`
@Override
protected LinkedList<Module> configureInjections() throws Exception {
final var modules = new LinkedList<Module>();
modules.add((binder) -> {
binder.bind(MyService.class).in(containerCallScope);
// @Inject Provider<MyService> myServiceProvider;
// will now work both in servlets and endpoints
// more bindings here...
});
return modules;
}
@Override
protected void configureServletsFiltersEndpoints() throws ServletException, DeploymentException
{
addEnsureSessionFilter("/websocket/*");
// MyServlet and MyProgrammaticEndpoint instances will have their dependencies injected
addServlet("myServlet", MyServlet.class, "/myServlet");
addEndpoint(MyProgrammaticEndpoint.class, "/websocket/myProgrammaticSocket");
// more servlets / filters / unannotated endpoints here...
}
}
NOTE: If the servlet container being used uses mechanism other than the standard Java Serialization to persist/replicate HttpSession
s, then a deployment init-param named pl.morgwai.base.servlet.guice.scopes.HttpSessionContext.customSerialization
must be set to true
either in web.xml
or programmatically before any request is served (for example in ServletContextListener.contextInitialized(event)
).
Note: in cases where it is not possible to extend GuiceServletContextListener
, all the configuration required to use ServletModule
(with all its Scopes
etc) and GuiceServerEndpointConfigurator
/ PingingEndpointConfigurator
, can be done manually: see an example in ManualServletContextListener.
Note: for GuiceServerEndpointConfigurator
to work, app's ServletContextListener
still needs to extend either GuiceServletContextListener
or PingingServletContextListener
as in the example above, even if there are no programmatic Servlet
s nor Endpoint
s.
@ServerEndpoint(
value = "/websocket/myAnnotatedSocket",
configurator = GuiceServerEndpointConfigurator.class) // ...or PingingEndpointConfigurator
public class MyAnnotatedEndpoint {
@Inject Provider<MyService> myServiceProvider; // will be injected automatically
// endpoint implementation here...
}
class MyComponent {
@Inject ContextBinder ctxBinder;
void methodThatCallsSomeAsyncMethod(/* ... */) {
// other code here...
someAsyncMethod(arg1, /* ... */ argN, ctxBinder.bindToContext((callbackParam) -> {
// callback code here...
}));
}
}
NOTE: when dispatching work to servlet container threads using any of AsyncContext.dispatch()
methods, the context is transferred automatically.
Dependencies of this jar on guice is declared as optional, so that apps can use any version with compatible API.
There are 2 builds available:
- build with
shadedbytebuddy
classifier includes relocated dependency on byte-buddy. Most apps should use this build. To do so, add<classifier>shadedbytebuddy</classifier>
to your dependency declaration. - "default" build does not include any shaded dependencies and dependency on
byte-buddy
is marked asoptional
. This is useful for apps that also depend onbyte-buddy
and need to save space (byte-buddy
is over 3MB in size). Note that the version provided by the app needs to be compatible with the version thatservlet-scopes
depends on (in regard to features used byservlet-scopes
). If this is not the case, thenshadedbytebuddy
build should be used.
Tyrus connection proxy that provides unified, websocket API compliant access to clustered websocket connections and properties.
a trivial sample app built from the test code.
Why isn't this built on top of official servlet scopes lib?
- the official Guice-servlet has some serious issues
- in order to extend the official Guice-servlet lib to support websockets, the code would need to pretend that everything is an
HttpServletRequest
(websocket events and websocket connections would need to be wrapped in some fakeHttpSevletRequest
wrappers), which seems awkward. guice-context-scopes
is thread-safe: a single request can be handled by multiple threads (as long as accessed scoped objects are thread-safe or properly synchronized).guice-context-scopes
allows to remove objects from scopes.
Why do I have to install myself a filter that creates HTTP session for websocket requests? Can't addEnsureSessionFilter("/*")
be called automatically?
Always enforcing a session creation is not acceptable in many cases, so this would limit applicability of this lib. Reasons may be technical (cookies disabled, non-browser clients that don't even follow redirections), legal (user explicitly refusing any data storage) and probably others. It's a sad trade-off between applicability and API safety.