I have a List<entity> ctx of my entity class in my java web application(tomcat8, servlet3), I keep one instance for each session(user) with my context(list), and a reference copy in user's sessions, like following.
javax.servlet.http.HttpSession sess=request.getSession(true);
//declaring and initializing the entity object
entity e=new entity();
e.timestamp=System.currentTimeMillis();
//keep the e with the session
sess.setAttribute("e",e);
//and a reference copy with another context
ctx.add(e);
Question:
I just want to know(an event, listener, ...) about user http session, in order to remove the entity object from the ctx when session gets expired(removed).Now how may I realized that a session is being expired from the server?
Just use a HttpSessionListener. It exists specially for that usage. Simply override the sessionDestroyed(HttpSessionEvent se) :
#Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
// do your processing
}
#Override
public void sessionCreated(HttpSessionEvent se) {
// empty implementation if you do not need it ...
}
Do not forget to declare it. Extract from javadoc : In order to receive these notification events, the implementation class must be either declared in the deployment descriptor of the web application, annotated with WebListener, or registered via one of the addListener methods defined on ServletContext.
Related
I need to use some session-scope data in an asynchronous job. The job is run with Spring #Async annotation.
To achieve this goal, request attributes are first remembered in a request thread:
currentAttributes = RequestContextHolder.getRequestAttributes();
Then, in a thread running the async job, the request attributes are restored:
RequestContextHolder.setRequestAttributes(currentAttributes);
Almost everything works - session beans are available. However, I cannot access the HttpSession object - to check if the session is still valid.
In a session-scoped bean:
#Autowired
transient private HttpSession session;
//...
private boolean isSessionInvalid() {
try {
session.getCreationTime();
return false;
} catch (IllegalStateException e) {
return true;
}
}
The call session.getCreationTime() throws an IllegalStateException, even when the session is still valid:
java.lang.IllegalStateException: The request object has been recycled and is no longer associated with this facade
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:904)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:366)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:361)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:307)
What can I do to make the session available from an async job? Or, alternatively, is there any other way to check that the session is still valid?
We're developing a SaaS solution for several consumers. This solution is based on Spring, Wicket and Hibernate. Our
database contains data from several customers. We've decided to model the database as follows:
public
Shared data between all customers, for example user accounts as we do not know which customer a user belongs to
customer_1
customer_2
...
To work with this setup we use a multi-tenancy setup with the following TenantIdentifierResolver:
public class TenantProviderImpl implements CurrentTenantIdentifierResolver {
private static final ThreadLocal<String> tenant = new ThreadLocal<>();
public static void setTenant(String tenant){
TenantProviderImpl.tenant.set(tenant);
}
#Override
public String resolveCurrentTenantIdentifier() {
return tenant.get();
}
#Override
public boolean validateExistingCurrentSessions() {
return false;
}
/**
* Initialize a tenant by storing the tenant identifier in both the HTTP session and the ThreadLocal
*
* #param String tenant Tenant identifier to be stored
*/
public static void initTenant(String tenant) {
HttpServletRequest req = ((ServletWebRequest) RequestCycle.get().getRequest()).getContainerRequest();
req.getSession().setAttribute("tenant", tenant);
TenantProviderImpl.setTenant(tenant);
}
}
The initTenant method is called by a servlet filter for every request. This filter is processed before a connection
is opened to the database.
We've also implemented a AbstractDataSourceBasedMultiTenantConnectionProviderImpl which is set as our
hibernate.multi_tenant_connection_provider. It issues a SET search_path query before every request. This works like charm for requests passing through the servlet filter described above.
And now for our real problem: We've got some entrypoints into our application which do not pass the servlet filter,
for instance some SOAP-endpoints. There are also timed jobs that are executed which do not pass the servlet filter.
This proves to be a problem.
The Job/Endpoint receives a value somehow which can be used to identify which customer should be associated with the
Job/Endpoint-request. This unique value is often mapped in our public database schema. Thus, we need to query the
database before we know which customer is associated. Spring therefore initializes a complete Hibernate session. This
session has our default tenant ID and is not mapped to a specific customer. However, after we've resolved the unique
value to a customer we want the session to change the tenant identifier. This seems to not be supported though, there
is no such thing as a HibernateSession.setTenantIdentifier(String) whereas there is a
SharedSessionContract.getTenantIdentifier().
We thought we had a solution in the following method:
org.hibernate.SessionFactory sessionFactory = getSessionFactory();
org.hibernate.Session session = null;
try
{
session = getSession();
if (session != null)
{
if(session.isDirty())
{
session.flush();
}
if(!session.getTransaction().wasCommitted())
{
session.getTransaction().commit();
}
session.disconnect();
session.close();
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
}
catch (HibernateException e)
{
// NO-OP, apparently there was no session yet
}
TenantProviderImpl.setTenant(tenant);
session = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
return session;
This method however does not seem to work in the context of a Job/Endpoint and leads to HibernateException such as
"Session is closed!" or "Transaction not succesfully started".
We're a bit lost as we've been trying to find a solution for quite a while now. Is there something we've misunderstood?
Something we've misinterpreted? How can we fix the problem above?
Recap: HibernateSession-s not created by a user request but rather by a timed job or such do not pass our servlet
filter and thus have no associated tenant identifier before the Hibernate session is started. They have unique values
which we can translate to a tenant identifier by querying the database though. How can we tell an existing Hibernate
session to alter it's tenant identifier and thus issue a new SET search_path statement?
We've never found a true solution for this problem, but chimmi linked to a Jira-ticket were others have requested such a feature: https://hibernate.atlassian.net/browse/HHH-9766
As per this ticket, the behavior we want is currently unsupported. We've found a workaround though, as the number of times we actually want to use this feature is limited it is feasible for us to run these operations in separate threads using the default java concurrency implementation.
By running the operation in a separate thread, a new session is created (as the session is threadbound). It is very important for us to set the tenant to a variable shared across threads. For this we have a static variable in the CurrentTenantIdentifierResolver.
For running an operation in a separate thread, we implement a Callable. These callables are implemented as Spring-beans with scope prototype so a new instance is created for each time it is requested (autowired). We've implemented our own abstract implementation of a Callable which finalizes the call()-method defined by the Callable interface, and the implementation starts a new HibernateSession. The code looks somewhat like this:
public abstract class OurCallable<TYPE> implements Callable<TYPE> {
private final String tenantId;
#Autowired
private SessionFactory sessionFactory;
// More fields here
public OurCallable(String tenantId) {
this.tenantId = tenantId;
}
#Override
public final TYPE call() throws Exception {
TenantProvider.setTenant(tenantId);
startSession();
try {
return callInternal();
} finally {
stopSession();
}
}
protected abstract TYPE callInternal();
private void startSession(){
// Implementation skipped for clarity
}
private void stopSession(){
// Implementation skipped for clarity
}
}
Another workaround I've found thanks to #bas-dalenoord comment regarding OpenSessionInViewFilter/OpenEntityManagerInViewInterceptor which led me to this direction, is to disable this interceptor.
This can be achieved easily by setting spring.jpa.open-in-view=false either in the application.properties or environment-variable.
OpenEntityManagerInViewInterceptor binds a JPA EntityManager to the thread for the entire processing of the request and in my case it's redundant.
Another workaround is to break the request that needs to make DB calls on behalf of 2 different tenants into 2 separate requests.
First the client ask for his associated tenant in the system, and then creates a new request with the given tenant as a parameter. IMO, until (and if) the feature will be supported, it's a relatively clean alternative.
I want to develop a utility class, that can be used in a static manner (static methods), for an enterprise Java system (JSP, Servlets, EJBs). It'd contain some methods that is capable to access the HttpSession object of the user and retrieves some useful info that is already stored as attributes (such as the current user id).
Some of you may wonder why do I need such thing, while I can simply passing the HttpSession object to anywhere. Actually, I am working on a legacy Java EE 5.0 system and some of the utility classes (not Servlets, JSPs, nor EJBs) have no access to the HttpSession object.
Is it possible to implement such utility class? Some constraints I/you should consider here:
The servlets/JSPs are hosted on machines that are different from what host EJBs.
The system is running on Weblogic 10.3.0.
On weblogic, there are many servers (that host servlets/JSPs) and they are under the same cluster. The same thing is with EJBs servers.
If I declare some static Collection inside the utility class, is it going to work? or maybe there will be more than one copy of it because of the multiple class loaders and multiple JVMs?
Maybe I should use a shared file or shared database to implement it? How even could I track which user invokes the utility class? Maybe tracking the thread? or maybe something related to the transaction?
Make use of ThreadLocal. You should only not store the HttpSession directly in there. The service layer should not have any dependency on javax.servlet API. Instead, extract the desired information from the HttpSession directly and store it there.
E.g. when you want to expose User attribute of the HttpSession as a thread local variable:
public class SomeContext {
private static ThreadLocal<SomeContext> instance = new ThreadLocal<SomeContext>();
private User user;
private SomeContext(User user) {
this.user = user;
}
public static SomeContext getCurrentInstance() {
return instance.get();
}
public static SomeContext newInstance(User user) {
SomeContext someContext = new SomeContext(user);
instance.set(someContext);
return someContext;
}
public void release() {
instance.remove();
}
public User getUser() {
return user;
}
}
and this in doFilter() of a servlet filter:
User user = (User) request.getSession().getAttribute("user");
SomeContext someContext = SomeContext.newInstance(user);
try {
chain.doFilter(request, response);
} finally {
// It's very important to do this in finally!
// Threads are namely pooled by the container.
someContext.release();
}
in any code which is running in the very same thread after the particular filter, including EJBs, you can get the User as follows:
User user = SomeContext.getCurrentInstance().getUser();
// ...
The HttpSession is local to a Thread.
Do you really need an HttpSession or could you just do what you want with a static ThreadLocal attribute?
See: http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
btw, Apache Shiro uses this class to store some "Session" information.
When developing a JSP application it's possible to define a session timeout value, say 30 minutes.
After that timeout, the session object is destroyed.
Moreover I can programmatically invalidate a session calling session.invalidate() .
Since I'm saving a complex Java object inside the HTTP session, before invalidate the session or let it expire by the tomcat app server, I need to call a saved object method to release some memory. Of course I can do it programmatically when the user click a logout button.
What I would like to do is intercepting the Tomcat app server when it is going to destroy all expired sessions (30 minutes or custom), so that I can pre-process Java objects saved in the session calling a specific method to release memory.
Is it possible?
Yes, that's possible. You could use HttpSessionListener and do the job in sessionDestroyed() method,
#WebListener
public class MyHttpSessionListener implements HttpSessionListener {
#Override
public void sessionDestroyed(HttpSessionEvent event) {
// Do here the job.
}
// ...
}
Or you could let the complex object which is been stored as a session attribute implement the HttpSessionBindingListener and do the job in valueUnbound() method.
public class YourComplexObject implements HttpSessionBindingListener {
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
// Do here the job.
}
// ...
}
It will be called whenever the object is to be removed from the session (either explicitly by HttpSession#removeAttribute() or by an invalidation/expire of the session).
How to get Hibernate session inside a Hibernate Interceptor?
I'm trying to use Hibernate to enforce data access by organization id transparently.
I have set a global Filter to filter all queries by organization id.
Now, I need to use an Entity interceptor to set Organizational Id on all Entities before Save/Update.
The organization id comes from HttpSession
I've set Organizational Id as a Filter property in Hibernate session which i want to retrieve inside my interceptor and use for all Inserts and Updates as well.
The problem is i dont seem to have access to Session inside the Interceptor. Any workarounds for this?
You can, but I would use a simple POJO just to keep things cleanly separated. Keep in mind that the value stored in the singleton will only be accessible by the same thread that handled the servlet request, so if you're doing any asynch, you will need to account for that. Here's a super basic impl:
public class OrgId {
public static ThreadLocal<Integer> orgId = new ThreadLocal<Integer>();
}
Since the Organizational Id is resident in the session, you could set the value of the ThreadLocal in an early servlet filter like this (not much error checking):
public class OrgIdFilter implements Filter {
public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws java.io.IOException, javax.servlet.ServletException {
int orgId = 0;
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpSession session = req.getSession();
orgId = Integer.parseInt(session.getAttribute("OrganizationalIdAttr"));
try {
OrgId.orgId.set(orgId);
filterChain.doFilter(servletRequest, servletresponse);
} finally {
OrgId.orgId.set(null); // Important to clear after request !!
}
}
}
This assumes that the orgId is in the session when the filter is called, but if not, you get the idea....
Then in your interceptor (or pretty much anywhere) you can get the thread's current orgId with:
OrgId.orgId.get(); // Might be null.....
A potential snafu here is that all these components (filter, OrgId and interceptor) need to be loaded by the same class loader to ensure that the OrgId class is effectively a singleton, otherwise, with multiple instances of the ThreadLocal hanging around it won't work consistently, or at all. Needless to say, all this needs to be happening in the same VM.
I am not sure if this is the cleanest way to solve this problem, but it does get you your orgId where you need it.
If all you need is the Organizational Id, you could put it in a static ThreadLocal and then access it in the interceptor.
On the other hand if you are dead set on getting the session, and this depends on what your environment is, you could ditch the interceptor and use an org.hibernate.event.FlushEntityEventListener which seems to be more along the lines of what you need anyways. You can get the session like this (rough pseudo code):
FlushEntityEventListener.onFlushEntity(FlushEntityEvent event)
EntityEvent entityEvent = event.getEntityEntry();
EntityPersister persister = entityEvent.getPersister();
SessionFactoryImplementor sessionFactoryImplor = persister.getFactory();
Session session = sessionFactoryImplor.getCurrentSession();
From the Hibernate 3 On Line Docs: The event system can be used in addition or as a replacement for interceptors.
When you create your Interceptor, if you can provide it with a reference to the SessionFactory, you can use SessionFactory#getCurrentSession
Interceptor can be made BeanFactoryAware and SessionFactory can be obtained using the bean factory from which current session can be obtained.
Since it seemed like a bad design because of the circular dependency and making the Interceptor aware of Spring container, i used ThreadLocal as suggested by Nicholas