I have been given a use-case, to come up with a solution to allow configured number of users per user id to be logged in to my application at any given time.
For example : userid 'parentuser' can be used to log in to the application for a max of 10 times at any time.After this limit, the user will not allowed to log in as max number of users are accessing the application for that user.
Now, To implement this, I have created a context listener which will instantiate an attribute which I'll keep updating as the user logs in the application in the Action class.
My Context Listener is as under :
public class ApplicationContextListener implements ServletContextListener {
private Map<String, List<ApplicationContextBean>> userMap;
#Override
public void contextDestroyed(ServletContextEvent arg0) {
userMap = null;
}
#Override
public void contextInitialized(ServletContextEvent event) {
userMap = new HashMap<String, List<ApplicationContextBean>>();
}
public Map<String, List<ApplicationContextBean>> getUserMap() {
return userMap;
}
public void setUserMap(Map<String, List<ApplicationContextBean>> userMap) {
this.userMap = userMap;
}
}
web.xml is as under
<listener>
<listener-class>com.pcs.bpems.portal.listener.ApplicationContextListener</listener-class>
</listener>
Question : How can I now access this context object 'userMap' from my action class? If anyone has any other approach different than this also, kindly post the same.
Thanks
The answer is in the title of your question: store the Map (or an object wrapping the map and providing useful methods) into an attribute of the servlet context (accessible from the event), and retrieve it from wherever you want: the HttpServletRequest provides access to the servlet context.
A better solution, which would also work in case your application is clustered, would be to use the database.
Also, don't forget to decrement the counter when the session expires.
This can be stored in the Servlet Context as under :
#Override
public void contextInitialized(ServletContextEvent event) {
userMap = new HashMap<String, Map<String,List<ApplicationContextBean>>>();
event.getServletContext().setAttribute(ApplicationConstants.LOGGED_IN_USERS, userMap);
}
The stored parameters can be then fetched from the HttpSession Object as under :
currentSession.getServletContext().getAttribute(LOGGED_IN_USERS)
Related
I am trying to implement a database read/write seperation using the spring class ( I used this tutorial)
AbstractRoutingDataSource
The application I am working on is using multinenacy and connection pooling (hikari). So I created a ( master/replica datasources for each tenant)
This is how I create the datasources
public DataSource RoutingDatasource(String tenantId,
String databaseMasterUrl,
String databaseReplicaUrl,
String user,
String password) {
final RoutingDataSource routingDataSource = new RoutingDataSource();
final DataSource masterDataSource = buildTargetDataSource(tenantId,
cachePrepStmtsValue,
prepStmtCacheSize,
prepStmtCacheSqlLimit,
databaseMasterUrl,
driverClass,
user,
password,
MASTER_DATASOURCE_PREFIX);
final DataSource replicaDataSource = buildTargetDataSource(poolName + tenantId,
cachePrepStmtsValue,
prepStmtCacheSize,
prepStmtCacheSqlLimit,
databaseReplicaUrl,
driverClass,
user,
password,
REPLICA_DATASOURCE_PREFIX);
final Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DbContext.DbType.MASTER, masterDataSource);
targetDataSources.put(DbContext.DbType.REPLICA, replicaDataSource);
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(masterDataSource);
routingDataSource.afterPropertiesSet();
return routingDataSource;
}
This is how the context is determined
public class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DbContext.getDbType();
}
and this is how I tell to transaction to switch context
#Transactional(readOnly = true)
public Opportunite consulter(UUID personUuid) {
DbContext.setDbType(DbContext.DbType.REPLICA);
//some work
DbContext.reset();
return some_result ;
}
The code is compiling fine but is not really switching context. In fact after debugging the problem was that the datasource was requested before the transaction is setup. When the transaction is finally setup, it is too late, the datasource has already been loaded.
How can I fix this behavior ? thank you.
To solve the problem I got to define which database to use before entring to the transaction to do so I have defined a filter ( I am using spring)
public class DbFilter extends {
//les urls that do not only GET method
private final static String NOT_ONLY_GET_URL= "/api/parametre";
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException, RestException {
DbContext.setDbType(DbContext.DbType.MASTER);
if (!NOT_ONLY_GET_URL.equals(request.getRequestURI()) && request.getMethod().equals("GET")) {
DbContext.setDbType(DbContext.DbType.REPLICA);
}
filterChain.doFilter(request, response);
}
This method is runned before calling any #transactional method and is switching the database if it is a GET method.
I have deleted
DbContext.setDbType(DbContext.DbType.REPLICA); and Dbcontext.reset() in the #transactional methods.
the "not only get urls parts" is only defined because in my application there are some get methods that do an update inside so I detected those urls and I affected them to the master databse .
Set spring.jpa.open-in-view to false
I own a spring application and want to add camel routes dynamically during my application startup.End points are configured in property file and are loaded at run time.
Using Java DSL, i am using for loop to create all routes,
for(int i=0;i<allEndPoints;i++)
{
DynamcRouteBuilder route = new
DynamcRouteBuilder(context,fromUri,toUri)
camelContext.addRoutes(route)
}
private class DynamcRouteBuilder extends RouteBuilder {
private final String from;
private final String to;
private MyDynamcRouteBuilder(CamelContext context, String from, String to) {
super(context);
this.from = from;
this.to = to;
}
#Override
public void configure() throws Exception {
from(from).to(to);
}
}
but getting below exception while creating first route itself
Failed to create route file_routedirect: at: >>> OnException[[class org.apache.camel.component.file.GenericFileOperationFailedException] -> [Log[Exception trapped ${exception.class}], process[Processor#0x0]]] <<< in route: Route(file_routedirect:)[[From[direct:... because of ref must be specified on: process[Processor#0x0]\n\ta
Not sure about it- what is the issue ? Can someone has any suggestion or fix for this. Thanks
Well, to create routes in an iteration it is nice to have some object that holds the different values for one route. Let's call this RouteConfiguration, a simple POJO with String fields for from, to and routeId.
We are using YAML files to configure such things because you have a real List format instead of using "flat lists" in property files (route[0].from, route[0].to).
If you use Spring you can directly transform such a "list of object configurations" into a Collection of objects using #ConfigurationProperties
When you are able to create such a Collection of value objects, you can simply iterate over it. Here is a strongly simplified example.
#Override
public void configure() {
createConfiguredRoutes();
}
void createConfiguredRoutes() {
configuration.getRoutes().forEach(this::addRouteToContext);
}
// Implement route that is added in an iteration
private void addRouteToContext(final RouteConfiguration routeConfiguration) throws Exception {
this.camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from(routeConfiguration.getFrom())
.routeId(routeConfiguration.getRouteId())
...
.to(routeConfiguration.getTo());
}
});
}
I am using Spring MVC and I would like to get some parameters from the request headers and store them an an object that is available anywhere in the application during the current request. Imagine a an application wide metadata class.
I would like to avoid using the RequestContextHolder because other parts of the application below the controller shouldn't care that the values came from the request. It should just be available.
I also thought of using a request scoped bean, but then I would have to wire it up everywhere I wanted to use it. I which goes against "just having it available"
Any info on where to start would be great.
Update:
This is my idea. I'm worried that when other requests come, they will set the transactionId of the the previous request.
Wire up a request scoped bean:
<bean id="metaDataContextHolder" class="com.yp.common.context.MetadataContextHolder" scope="request">
<aop:scoped-proxy/>
</bean>
Here is the request scoped bean
public class MetadataContextHolder {
private static String transactionId;
//removed other properties for brevity
public static String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
}
Capture the request headers in the filter to be stored for use throughout the application:
public class RequestMetatDataFilter implements Filter
{
#Autowired
MetadataContextHolder metaDataHolder;
#Override
public void init(FilterConfig arg0) throws ServletException
{
}
/**
* This filter will add common request metatdata to the MetaDataContextHolder
*/
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filerChain) throws IOException, ServletException
{
HttpServletRequest request = ((HttpServletRequest)servletRequest);
String transactionId = request.getHeader("transactionId");
metaDataHolder.setTransactionId(transactionId);
...
//store other values
...
filerChain.doFilter(request, response);
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Then the metadata can be accessed anywhere without having to wire the metadata bean again like:
MetaDataContextHolder.getTransactionId();
Update 2
As I suspected, the static property "transactionId" in MetadataContextHolder is updated by every request.
Is it possible to achieve what I am trying to do?
Two parts to this answer:
I would like to avoid using the RequestContextHolder because other
parts of the application below the controller shouldn't care that the
values came from the request. It should just be available.
Yes, this part makes sense for exactly the reason you just said.
I also thought of using a request scoped bean, but then I would have
to wire it up everywhere I wanted to use it. I which goes against
"just having it available"
Auto-wiring with request scoped beans is the way to go here. When you say you just want to have it available, it's not entirely clear what that means. It sounds like you want a singleton but for it to only contain the data for the current request, no matter who is accessing it. The request scoped beans are designed to hold only the current request data (as you know already), whereas singletons are not. I would actually considering autowiring a bean via annotation as "just having it available". Additionally having it as a bean rather than a singleton makes it much easier to test/mock when needed.
To store a request scoped object and be able to retrieve it in a static way, I needed to implement my own context holder and store the object in thread local.
I started by duplicating the code from RequestContextHolder but instead I parameterized my thread local object with the Metadata class.
public class MetadataContextHolder {
private static final ThreadLocal<Metadata> metadataHolder =
new NamedThreadLocal<Metadata>("Metadata Context");
private static final ThreadLocal<Metadata> inheritableMetadataHolder =
new NamedInheritableThreadLocal<Metadata>("Metadata Context");
/**
* Reset the metadata for the current thread.
*/
public static void resetMetadata() {
metadataHolder.remove();
}
/**
* Bind the given Metadata to the current thread,
* <i>not</i> exposing it as inheritable for child threads.
* #param metadata the Metadata to expose
* #see #setMetadata(Metadata, boolean)
*/
public static void setMetadata(Metadata metadata) {
setMetadata(metadata, false);
}
/**
* Bind the given Metadata to the current thread.
* #param metadata the Metadata to expose,
* or {#code null} to reset the thread-bound context
* #param inheritable whether to expose the Metadata as inheritable
* for child threads (using an {#link InheritableThreadLocal})
*/
public static void setMetadata(Metadata metadata, boolean inheritable) {
if (metadata == null) {
resetMetadata();
}
else {
if (inheritable) {
inheritableMetadataHolder.set(metadata);
metadataHolder.remove();
}
else {
metadataHolder.set(metadata);
inheritableMetadataHolder.remove();
}
}
}
/**
* Return the Metadata currently bound to the thread.
* #return the Metadata currently bound to the thread,
* or {#code null} if none bound
*/
public static Metadata getMetadata() {
Metadata metadata = metadataHolder.get();
if (metadata == null) {
metadata = inheritableMetadataHolder.get();
}
return metadata;
}
}
I then created a filter to populate the metadata context, but this can be populated anywhere really:
public class RequestMetatDataFilter implements Filter
{
#Override
public void init(FilterConfig arg0) throws ServletException
{
}
/**
* This filter will add common request metatdata to the MetadataContextHolder
*/
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filerChain) throws IOException, ServletException
{
HttpServletRequest request = ((HttpServletRequest)servletRequest);
String transactionId = request.getHeader("transactionId");
Metadata metadata = new Metadata(transactionId);
MetadataContextHolder.setMetadata(metadata);
filerChain.doFilter(request, response);
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
I then needed to register a request listener in my web.xml to clean up the thread local when the request completes:
<listener>
<listener-class>com.ws.listener.RequestListener</listener-class>
</listener>
Lastly I need to perform the cleanup in the request listener:
public class RequestListener implements ServletRequestListener {
/**
* Reset the metadata for the current request thread
*/
public void requestDestroyed(ServletRequestEvent event) {
MetadataContextHolder.resetMetadata();
}
/**
* Don't do anything when the request is initialized
*/
public void requestInitialized(ServletRequestEvent event) {
}
}
Here is the Metadata object for good measure:
public class Metadata {
private String idTransaccion;
//Other properties removed for brevity
public Metadata(String idTransaccion) {
super();
this.idTransaccion = idTransaccion;
}
public String getIdTransaccion() {
return idTransaccion;
}
public void setIdTransaccion(String idTransaccion) {
this.idTransaccion = idTransaccion;
}
}
Coming across the same issue right now. I need an easily & unobtrusively accessible id for each request. I'm going to solve it by creating a Bean with a Request Scope annotation, and using an Interceptor/Filter to populate it for each request. Then autowire that Bean in classes where I need it.
Reference link: http://www.baeldung.com/spring-bean-scopes
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"));
}
}
}