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
Related
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.
More specifically, I find that I'm implementing a custom AuthorizingRealm, which declares template methods doGetAuthenticationInfo() and doGetAuthorizationInfo() for returning AuthenticationInfo and AuthorizationInfo objects, respectively.
However, when I retrieve the data for the AuthenticationInfo (a JPA entity) in doGetAuthenticationInfo(), I find that I already have the necessary AuthorizationInfo. Alas, there's no apparantly good way to hang onto this data, so I have to throw it out only to perform another JPA lookup when the authorization filter ultimately gets its turn in the filter chain.
Behold:
public class CustomRealm extends AuthorizingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
UsernamePasswordToken userPassToken = (UsernamePasswordToken) token;
String username = userPassToken.getUsername()
User user; // Contains username, password, and roles
// Perform JPA lookup by username...
return constructSimpleAuthenticationInfoFromUser(user);
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// Look up user again? :(
...
}
}
I've considered a number of possibilities:
Use realm caching. The application will run in a distributed environment so there could be any arbitrary number of JVMs running. The default realm cache manager implementations don't solve all of the inherent problems and setting up an enterprise implementations seems out of scope for this project.
Use the subject's session. There is no server-side state and I'd like to keep it that way if possible. Perhaps you can force the session to behave like request scope, but I wouldn't know how to do so and that risks being obfuscated.
Implement my own Subject. There appears to typically be one Subject instance per request, but it's unclear how to bootstrap this and I would risk losing a lot of potential functionality.
Use the Shiro ThreadContext object. I could attach the data to the ThreadContext as a threadlocal property. Servlet containers generally follow a thread-per-request model, and the Subject instance itself seems to chill out here, awaiting its inevitable garbage collection. Shiro also appears to build up and tear down the context automatically. However, there's not much documentation on this and the source code is hard for me to follow.
Finally, the default WebSecurityManager keeps singleton instances of the CustomRealm around, one per JVM it seems. Simply setting some local instance property is not thread-safe.
This seems like a common data retrieval option and a typical deployment scenario. So, what am I missing?
Thanks!
I would go with option 4 - Using ThreadLocal object as your requirement clearly says that the object lifetime must be of http request.
Have a look at this discussion: When and how should I use a ThreadLocal variable?
ThreadLocal doc: http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
I'm developing a web-app using Struts 2 with a Spring 3 backend. I'm using Spring aop:proxy beans to handle my session beans rather than the Struts 2 SessionAware interface. Everything was working fine until I have an Action that is running under the Struts ExecAndWait interceptor. Because this interceptor in effect runs my action under a seperate thread, when I come to try and access my proxied session bean, I get a BeanCreationException/IllegalStateException. Is there another "spring way" that I can get hold of my session beans in this scenario?
Regards
From Execute and Wait Interceptor documentation
Important: Because the action will be running in a seperate thread, you can't use ActionContext because it is a ThreadLocal. This means if you need to access, for example, session data, you need to implement SessionAware rather than calling ActionContext.getSession().
The problem with session scoped-beans is that they depend on thread-local attributes set by RequestContextListener or RequestContextFilter. But the latter allows you to set very interesting threadContextInheritable flag...
If your ExecAndWait interceptor creates new thread per every request it serves, inheritable thread local should propagate session scoped beans to child threads. However if Struts uses thread pool (more likely, thou I haven't used Struts2 for ages) to serve this requests, this will have very unexpected and dangerous results. You might experiment with this flag, maybe it will do the trick.
You can Implement your own ExecAndWait interceptor using Spring. You can also delegate the management/creation of this action to Spring. For the later the details are in the S2 spring plugin documentation.
You can use ,RequestContextHolder(Holder class to expose the web request in the form of a thread-bound RequestAttributes object.) to make session scoped proxy beans available to child threads.
Define a custom ExecuteAndWait Interceptor and in doIntercept method use the following static method from RequestContextHolder
public static void setRequestAttributes(RequestAttributes attributes,boolean inheritable)
Bind the given RequestAttributes to the current thread.
Parameters:
attributes - the RequestAttributes to expose, or null to reset the thread-bound context
inheritable - whether to expose the RequestAttributes as inheritable for child threads (using an InheritableThreadLocal)
Sample Code
public class CustomExecuteAndWaitInterceptor extends ExecuteAndWaitInterceptor {
#Override
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
RequestAttributes requestAtteiAttributes = RequestContextHolder.getRequestAttributes(); //Return the RequestAttributes currently bound to the thread.
RequestContextHolder.setRequestAttributes(requestAtteiAttributes, true);
//do something else if you want ..
return super.doIntercept(actionInvocation);
}
}
I have developed a web application. I put session id in hashtable. I want to use this hash table in ejb. Means I want to use this login session in ejb.
Is it possible? How can I do that?
It's not impossible, but you must not do it. Get everything you need from the HttpSession and send it to the EJB as method parameters. For example:
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String foo = (String) request.getSession().getAttribute("foo");
ejbService.doSomething(foo);
}
So - pass the values you need as parameters to methods, through the chain layers of your application.
Security credentials are passed automatically from servlet to EJB. If your user has authenticated to the Web Container then you don't need to do any work yourelf.
In the session bean get the EjbContext as described link text
#Resource
private SessionContext sctx;
and then call methods such as
sctx.getCallerPrincipal();
I am writing some servlets with plain old mostly-JDBC patterns. I realized that I have several objects that would like to share a single transaction, and I'd like to enforce that one HTTP transaction = one database transaction.
I think I can do this via passing a Connection around in a ThreadLocal variable, and then having a servlet filter handling the creation/commit/rollback of said Connection.
Is there an existing framework that does this that I'm not privy to, or is this a reasonable late-00's way to do things?
Spring transaction management does exactly what you describe, it might be a little over whelming at first glance but all you will be needing (for the simplest case) is:
org.springframework.jdbc.datasource.DataSourceTransactionManager
org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
org.springframework.transaction.support.TransactionTemplate
Wire up your existing DataSource and wrap it in the TransctionAwareDataSourceProxy then create a DataSourceTransactionManager with the wrapped data source, keep these in your ServletContext. Then for each transaction create a TransactionTemplate passing in the transaction manager and call the execute(TransactionCallback) method to run your code. eg:
new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
public void doInTransaction(TransactionStatus ts){
// run your code here...use the dataSource to get a connection and run stuff
Connection c = dataSourceProxy.getConnection();
// to rollback ... throw a RuntimeException out of this method or call
st.setRollbackOnly();
}
});
The connection will be bound to a thread local so as long as you always get the connection form the same datasource i.e. the wrapped one, you'll get the same connection in the same transaction.
Note this is the simplest possible spring transaction setup ... not nessarly the best or recommended one, for that have a look at the spring reference doc's or read spring in action.
... so I guess as a direct answer, yes it is a reasonable thing to be doing, it's what the spring framework has been doing for a long time.
Most appServer todays support JTA (Java Transaction Api): A transaction that spans over multiple open/close jdbc connections. It does the "threadLocal" stuff for you and it's J2EE compliant.
You use it like this in your filter:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
UserTransaction transaction = null;
try {
transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
transaction.begin();
chain.doFilter(request, response);
transaction.commit();
} catch (final Exception errorInServlet) {
try {
transaction.rollback();
} catch (final Exception rollbackFailed) {
log("No ! Transaction failed !",rollbackFailed);
}
throw new ServletException(errorInServlet);
}
}
On the app-server, declare a Datasource with a jndi name, and use it in your code to retrieve a connection (do NOT make cx.commit(), cx.rollback() or cx.setAutocommit() stuff, it will interfere with JTA). You can open and close your connection several times in the same HTTP transaction, JTA will take care of it:
public void doingDatabaseStuff() throws Exception {
DataSource datasource = (DataSource)new InitialContext().lookup("/path/to/datasource");
Connection connection = datasource.getConnection();
try {
// doing stuff
} finally {
connection.close();
}
}
It is generally better to pass object with "Parameterisation from Above", the sleazing through with ThreadLocal. In the case of ServletFilter, an attribute of the ServletRequest would be an obvious place. The interface to non-servlet dependent code can extract the Connection to meaningful context.
If you cannot rely on a "real" app server and you want to avoid the not-so-lightweightness of Spring, using a filter to provide a connection, keep it on the thread and close it at the end of the request is indeed a practical and reasonable solution.
You would need some (essentially static) accessor class that allows to get() a connection and a setRollbackOnly().
Upon end of the request, from the filter's perspective, make sure to catch exceptions (upon which you should log and set to rollback only) and commit/rollback, close the transaction accordingly.
In most applications and web containers (and JTA usually makes similar assumptions) a request will be processed by exactly one thread and associating the one database connection with the thread for re-use between layers during the request is just the right thing to do.
Having a filter manage the transaction is a good approach to rolling your own transaction management.
The Java EE specification provides for transaction management, and alternative frameworks like Spring provide similar support (though that's not an endorsement; Spring doesn't necessarily do this well).
However, use of a ThreadLocal can create problems. For example, there are no guarantees that a single thread is used throughout a request, anything can access the Connection through the global variable, and testing can become more difficult if you are depending on some global state to be set up. I'd consider using a dependency injection container to explicitly pass a Connection to objects that need one.