I have service that is called both from Quartz (w/o a session scope), and interactively from a Web Flow (w/ a session scope).
When called from the Web Flow, there may be some optional flags set and I would like to safely check for them, IF THEY EXIST.
I'm using session scope because the functions I'm calling are far down a call chain, and passing the options, or scope all the way down will touch a lot of code.
I'm wondering if there is something like:
if(someObject.session?.myFlag)
where "session" refers to the session scope if called from a web flow, or null if called from Quartz.
Thanks in advance!
As you seem to already know, accessing the session scope from a service is not something to be encouraged, because the HTTP session should only be used within the web layer (GSPs, controllers, filters, etc.).
Now you didn't hear this from me, but you can access the current session from anywhere like this:
def session = org.codehaus.groovy.grails.web.util.WebUtils.
retrieveGrailsWebRequest().session
Once you have access to the session you can check if attributes exist or retrieve them using the usual HttpSession API.
I would go with a Filter and ThreadLocal approach.
Create a class with a static ThreadLocal variable which holds a reference to an instance of the class. This instance can then be referenced from anywhere from the executing thread and it will provide access to your variables and flags. This way you are not directly referencing to HTTP session API in your service.
Finally create a Filter in which you set up the ThreadLocal value before executing the rest of the chain. Remember to clear the state of the value after the thread is complete.
class MyExecutionContext {
private static ThreadLocal instance = new ThreadLocal<MyExecutionContext>()
private HttpSession session
private ServletRequest request
// set the state for current thread
// you can add request here too, if you want/need
public static void setContext(ServletRequest req, HttpSession s) {
stateInstance.set(new MyExecutionContext(req, s))
}
// get the state of current thread
public static getContext() {
return instance.get()
}
// clear the current state
public static void clearContext() {
stateInstance.remove()
}
// private constructor
private MyExecutionContext(ServletRequest req, HttpSession s) {
request = req
session = s
}
// now the actual methods to query any kinds of things you need
// from session (or request if you gave it in the constructor)
public String getSomething() {
(String) session?.getAttribute("somethingInSession")
}
public String getSomethingElse() {
(String) request?.getAttribute("somethingInRequest")
}
}
class ContextFilter extends Filter {
public void doFilter(...) {
try {
MyExecutionContext.setContext(request, request.getSession(false))
chain.doFilter(req, res)
} finally {
// make sure you clear the state
MyExecutionContext.clearContext()
}
}
}
// usage in your service
class YourService {
def method() {
if (MyExecutionContext?.context?.something) {
// value exists in session
}
}
}
Related
I am using spring security. I was wondering if in a filter I set
SecurityContextHolder.getcontext().setAuthentication(null)
for one particular user who has at that instance suppose 100 api calls, would each of these calls securitycontextholder's authentication become null?
SecurityContextHolder is threadsafe by default. So using this statement SecurityContextHolder.getcontext().setAuthentication(null) would actually be unsetting authentication on a per-thread basis.
If you see the implementation of SecurityContextHolder.getcontext(), you will find that getContext() is actually returning a thread safe object:
final class ThreadLocalSecurityContextHolderStrategy implements
SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
//.....
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
}
As you can see in above code, contextHolder is actually the ThreadLocal object, which are by default thread safe variables.
So by changing values using SecurityContextHolder.getcontext().setAuthentication(null) will not effect authentication object of other threads(Note: Each & every web request are handled by separate threads).
In the olden days, we had ThreadLocal for programs to carry data along with the request path since all request processing was done on that thread and stuff like Logback used this with MDC.put("requestId", getNewRequestId());
Then Scala and functional programming came along and Futures came along and with them came Local.scala (at least I know the twitter Futures have this class). Future.scala knows about Local.scala and transfers the context through all the map/flatMap, etc. etc. functionality such that I can still do Local.set("requestId", getNewRequestId()); and then downstream after it has travelled over many threads, I can still access it with Local.get(...)
Soooo, my question is in Java, can I do the same thing with the new CompletableFuture somewhere with LocalContext or some object (not sure of the name) and in this way, I can modify Logback MDC context to store it in that context instead of a ThreadLocal such that I don't lose the request id and all my logs across the thenApply, thenAccept, etc. etc. still work just fine with logging and the -XrequestId flag in Logback configuration.
EDIT:
As an example. If you have a request come in and you are using Log4j or Logback, in a filter, you will set MDC.put("requestId", requestId) and then in your app, you will log many log statements line this:
log.info("request came in for url="+url);
log.info("request is complete");
Now, in the log output it will show this:
INFO {time}: requestId425 request came in for url=/mypath
INFO {time}: requestId425 request is complete
This is using a trick of ThreadLocal to achieve this. At Twitter, we use Scala and Twitter Futures in Scala along with a Local.scala class. Local.scala and Future.scala are tied together in that we can achieve the above scenario still which is very nice and all our log statements can log the request id so the developer never has to remember to log the request id and you can trace through a single customers request response cycle with that id.
I don't see this in Java :( which is very unfortunate as there are many use cases for that. Perhaps there is something I am not seeing though?
If you come across this, just poke the thread here
http://mail.openjdk.java.net/pipermail/core-libs-dev/2017-May/047867.html
to implement something like twitter Futures which transfer Locals (Much like ThreadLocal but transfers state).
See the def respond() method in here and how it calls Locals.save() and Locals.restort()
https://github.com/simonratner/twitter-util/blob/master/util-core/src/main/scala/com/twitter/util/Future.scala
If Java Authors would fix this, then the MDC in logback would work across all 3rd party libraries. Until then, IT WILL NOT WORK unless you can change the 3rd party library(doubtful you can do that).
My solution theme would be to (It would work with JDK 9+ as a couple of overridable methods are exposed since that version)
Make the complete ecosystem aware of MDC
And for that, we need to address the following scenarios:
When all do we get new instances of CompletableFuture from within this class? → We need to return a MDC aware version of the same rather.
When all do we get new instances of CompletableFuture from outside this class? → We need to return a MDC aware version of the same rather.
Which executor is used when in CompletableFuture class? → In all circumstances, we need to make sure that all executors are MDC aware
For that, let's create a MDC aware version class of CompletableFuture by extending it. My version of that would look like below
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Supplier;
public class MDCAwareCompletableFuture<T> extends CompletableFuture<T> {
public static final ExecutorService MDC_AWARE_ASYNC_POOL = new MDCAwareForkJoinPool();
#Override
public CompletableFuture newIncompleteFuture() {
return new MDCAwareCompletableFuture();
}
#Override
public Executor defaultExecutor() {
return MDC_AWARE_ASYNC_POOL;
}
public static <T> CompletionStage<T> getMDCAwareCompletionStage(CompletableFuture<T> future) {
return new MDCAwareCompletableFuture<>()
.completeAsync(() -> null)
.thenCombineAsync(future, (aVoid, value) -> value);
}
public static <T> CompletionStage<T> getMDCHandledCompletionStage(CompletableFuture<T> future,
Function<Throwable, T> throwableFunction) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return getMDCAwareCompletionStage(future)
.handle((value, throwable) -> {
setMDCContext(contextMap);
if (throwable != null) {
return throwableFunction.apply(throwable);
}
return value;
});
}
}
The MDCAwareForkJoinPool class would look like (have skipped the methods with ForkJoinTask parameters for simplicity)
public class MDCAwareForkJoinPool extends ForkJoinPool {
//Override constructors which you need
#Override
public <T> ForkJoinTask<T> submit(Callable<T> task) {
return super.submit(MDCUtility.wrapWithMdcContext(task));
}
#Override
public <T> ForkJoinTask<T> submit(Runnable task, T result) {
return super.submit(wrapWithMdcContext(task), result);
}
#Override
public ForkJoinTask<?> submit(Runnable task) {
return super.submit(wrapWithMdcContext(task));
}
#Override
public void execute(Runnable task) {
super.execute(wrapWithMdcContext(task));
}
}
The utility methods to wrap would be such as
public static <T> Callable<T> wrapWithMdcContext(Callable<T> task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.call();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static Runnable wrapWithMdcContext(Runnable task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.run();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static void setMDCContext(Map<String, String> contextMap) {
MDC.clear();
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
}
Below are some guidelines for usage:
Use the class MDCAwareCompletableFuture rather than the class CompletableFuture.
A couple of methods in the class CompletableFuture instantiates the self version such as new CompletableFuture.... For such methods (most of the public static methods), use an alternative method to get an instance of MDCAwareCompletableFuture. An example of using an alternative could be rather than using CompletableFuture.supplyAsync(...), you can choose new MDCAwareCompletableFuture<>().completeAsync(...)
Convert the instance of CompletableFuture to MDCAwareCompletableFuture by using the method getMDCAwareCompletionStage when you get stuck with one because of say some external library which returns you an instance of CompletableFuture. Obviously, you can't retain the context within that library but this method would still retain the context after your code hits the application code.
While supplying an executor as a parameter, make sure that it is MDC Aware such as MDCAwareForkJoinPool. You could create MDCAwareThreadPoolExecutor by overriding execute method as well to serve your use case. You get the idea!
You can find a detailed explanation of all of the above here in a post about the same.
Currently I have a simple custom policy handler that looks like so:
protected override void Handle(AuthorizationContext context, UserPolicyRequirement requirement)
{
// authorize user against policy requirements
if (_authorizationTask.AuthorizeUserAgainstPolicy(context.User, requirement))
{
// User passed policy req's
context.Succeed(requirement);
}
}
Problem is, this authorization step takes a long time to execute, but this is required in many different areas of the website. Is there any readily available mechanisms to save/cache the results of this policy authorization so that I only need to do this once per session?
I am currently using Windows Authentication, if that helps.
If per session way does not cause any problem, you can use Session to store user data. Simple implementation is something like below:
First you need a service to get user data from any store
public interface IGetUserDataService
{
<type> GetUserData();
}
I assume that there is Session configuration(see) and IGetUserDataService implementation.
Then you need to create a middleware to handle Session
public class SessionMiddleware
{
private readonly RequestDelegate _next;
private readonly IGetUserDataService _getUserDataService;
public SessionMiddleware(RequestDelegate next, IGetUserDataService getUserDataService)
{
_next = next;
_getUserDataService = getUserDataService;
}
public async Task Invoke(HttpContext context)
{
//user data is obtained only once then is stored in Session
if (context.Session.Get("UserData") == null)
{
context.Session.Set("UserData", getUserDataService.GetData());
}
await _next.Invoke(context);
}
}
//In Startup.cs
app.UseMiddleware<SessionMiddleware>();
Finally get and use session data in handler
public class YourHandler : AuthorizationHandler<YourRequirement>
{
private readonly IHttpContextAccessor _accessor;
public YourHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
var userData =(<type>)_accessor.HttpContext.Session.Get("UserData");
// check
}
}
I've searched quite a bit and been unable to find this: Is there a way that a spring websocket stomp server can disconnect a client based on the sessionId (or really based on anything at all)?
It seems to me that once a client connects to a server there is nothing that allows the server to disconnect the client.
Actually using some workarounds you can achieve what you want.
For that you should do:
Use java configuration (not sure if it is possible with XML config)
Extend your config class from WebSocketMessageBrokerConfigurationSupport and implement WebSocketMessageBrokerConfigurer interface
Create custom sub-protocol websocket handler and extend it from SubProtocolWebSocketHandler class
In your custom sub-protocol websocket handler override afterConnectionEstablished method and you will have access to WebSocketSession :)
I've created sample spring-boot project to show how we can disconnect client session from server side:
https://github.com/isaranchuk/spring-websocket-disconnect
You can also disconnect session by implementing a custom WebSocketHandlerDecorator:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig<S extends ExpiringSession> extends AbstractSessionWebSocketMessageBrokerConfigurer<S> {
#Override
public void configureWebSocketTransport(final WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
#Override
public WebSocketHandler decorate(final WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler) {
#Override
public void afterConnectionEstablished(final WebSocketSession session) throws Exception {
session.close(CloseStatus.NOT_ACCEPTABLE);
super.afterConnectionEstablished(session);
}
};
}
});
super.configureWebSocketTransport(registration);
}
#Override
protected void configureStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/home")
.setHandshakeHandler(new DefaultHandshakeHandler(
new UndertowRequestUpgradeStrategy() // If you use undertow
// new JettyRequestUpgradeStrategy()
// new TomcatRequestUpgradeStrategy()
))
.withSockJS();
}
}
As far as I know the API doesn't provide what you are looking for, on server-side you can only detect disconnect events. If you want to disconnect a certain client I think you must go for a litte workaround, e.g. this one:
Write a client-side javascript function that is able to trigger a disconnect
As soon as your client is connected to the server, generate a client ID in your javascript and send it to the server. Remember the ID on the client, you'll need it in step (4).
At the time you want the server to disconnect the connection to the specific client (identified by the ID), send a message containing the ID back to the client.
Now your client javascript evaluates the message send from the server and decides to call the disconnect function you wrote in step (1).
Your client disconnects itself.
The workaround is a bit cumbersome but it'll work.
I relied on the idea of #Dániel Kis and implemented the websocket session management with the key point of storing websocket sessions for authenticated users in Singleton-like object.
// WebSocketConfig.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
#Override
public WebSocketHandler decorate(final WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler) {
#Override
public void afterConnectionEstablished(final WebSocketSession session) throws Exception {
// We will store current user's session into WebsocketSessionHolder after connection is established
String username = session.getPrincipal().getName();
WebsocketSessionHolder.addSession(username, session);
super.afterConnectionEstablished(session);
}
};
}
});
}
}
Class to store websocket users' sessions WebsocketSessionHolder. I use 'synchronized' blocks for thread safety. Actually this blocks are not expensive operations because each of methods (addSession and closeSessions) are used not so often (On establishing and terminating connection). No need to use ConcurrentHashMap or SynchronizedMap here because we perform bunch of operations with the list in these methods.
// WebsocketSessionHolder.java
public class WebsocketSessionHolder {
static {
sessions = new HashMap<>();
}
// key - username, value - List of user's sessions
private static Map<String, List<WebSocketSession>> sessions;
public static void addSession(String username, WebSocketSession session)
{
synchronized (sessions) {
var userSessions = sessions.get(username);
if (userSessions == null)
userSessions = new ArrayList<WebSocketSession>();
userSessions.add(session);
sessions.put(username, userSessions);
}
}
public static void closeSessions(String username) throws IOException
{
synchronized (sessions) {
var userSessions = sessions.get(username);
if (userSessions != null)
{
for(var session : userSessions) {
// I use POLICY_VIOLATION to indicate reason of disconnecting for a client
session.close(CloseStatus.POLICY_VIOLATION);
}
sessions.remove(username);
}
}
}
}
And the final touch - terminating (disconnecting) specified user websocket sessions ("ADMIN" in the example), say in some Controller
//PageController.java
#Controller
public class PageController {
#GetMapping("/kill-sessions")
public void killSessions() throws Exception {
WebsocketSessionHolder.closeSessions("ADMIN");
}
}
In case of xml configuration you can use <websocket:decorator-factories> in the <websocket:transport> of your <websocket:message-broker>.
Create custom WebSocketHandlerDecorator and WebSocketHandlerDecoratorFactory which implement decorate method.
This may seem brief but I am not certain what the implementation would look like in your case. But, I think there are some circumstances that would warrant this workaround/solution:
Set a timeout on the back-end (say 30 seconds):
This is how you would do it with Spring Boot Websocket (and Tomcat):
#Bean
public ServletServerContainerFactoryBean websocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxSessionIdleTimeout(MAX_SESSION_IDLE_TIMEOUT);
return container;
}
If you want to keep the session open - continue to send messages or else actively send ping/pongs. In the case that you want the session to disconnect, stop the ping/pong interaction somewhere suitable in you application.
Of course, if you are wanting to disconnect immediately, this doesn't seem to be an appropriate solution. But if you are simply trying to reduce the number of active connections, ping/pong may be a good fit since it keeps a session open only so long as messages are actively being sent, preventing the session from being closed prematurely.
first you have to introduce a class as your User class by inheritance then use it like this:
if (userObject instanceof User) {
User user = (User) userObject;
if (user.getId().equals(userDTO.getId())) {
for (SessionInformation information : sessionRegistry.getAllSessions(user, true)) {
information.expireNow();
}
}
}
I'm doing a project in Vaadin 7. In that I need to implement something like below for the login.
A user 'A' is logged in to a system '1'. And again he logs into another system '2'. Now I want to know how to close the UI on the system '1'.
I tried something and can able to close the UI, If it is the same browser. But, for different systems/browser. I don't have any idea.
My Code:
private void closeUI(String attribute) {
for (UI ui : getSession().getUIs()) {
if(ui.getSession().getAttribute(attribute) != null)
if(ui.getSession().getAttribute(attribute).equals(attribute))
ui.close();
}
}
Can anyone help me in this?
I have a situation similar to your where I need to display several info regarding all sessions. What I did was I created my own Servlet extending the VaadinServlet with a static ConcurrentHashmap to save my sessions info, and a SessionDestroyListener to remove any info from the map upon logout. Initially I also had a SessionInitListener where I added the info in the hashmap but I realized I only had the user information after authentication so I moved this part to the page handling the login.
I guess you could do something similar, or at least this should get you started:
public class SessionInfoServlet extends VaadinServlet {
private static final ConcurrentHashMap<User, VaadinSession> userSessionInfo = new ConcurrentHashMap<>();
// this could be called after login to save the session info
public static void saveUserSessionInfo(User user, VaadinSession session) {
VaadinSession oldSession = userSessionInfo.get(user);
if(oldSession != null){
// close the old session
oldSession.close();
}
userSessionInfo.put(user, session);
}
public static Map<User, VaadinSession> getUserSessionInfos() {
// access the cache if we need to, otherwise useless and removable
return userSessionInfo;
}
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
// register our session destroy listener
SessionLifecycleListener sessionLifecycleListener = new SessionLifecycleListener();
getService().addSessionDestroyListener(sessionLifecycleListener);
}
private class SessionLifecycleListener implements SessionDestroyListener {
#Override
public void sessionDestroy(SessionDestroyEvent event) {
// remove saved session from cache, for the user that was stored in it
userSessionInfo.remove(event.getSession().getAttribute("user"));
}
}
}