In order to enable lazy fetch across all pages of our web app, we have a custom SessionFactory that maps Hibernate Session to HTTPSession. The session is flushed every time a page is sent to JSP rendering. We’ve also made a proxy for Transaction to deal with the browser fetching our pages in multiple threads. Is there a way to get the Session ID that the lazy proxy is using to fetch needed embeddables?
Crash log (session here is from Session.getSessionIdentifier) of logging in and then clicking one of the tabs:
New HttpSession CE5DB17C075F12D3ED0D62EC5FF44DCB
11:21:21.851 [http-nio-8084-exec-55] WARN org.hibernate.orm.connections.pooling - HHH10001002: Using Hibernate built-in connection pool (not for production use!)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@5baf74ec[Locked by thread http-nio-8084-exec-55] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@5baf74ec[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Locked by thread http-nio-8084-exec-56] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@3c7db4e1[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@51f280ec[Locked by thread http-nio-8084-exec-56] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@51f280ec[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@51f280ec[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@51f280ec[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@51f280ec[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@51f280ec[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@3d92935[Locked by thread http-nio-8084-exec-61] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@3d92935[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@5b0368b4[Locked by thread http-nio-8084-exec-60] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@5b0368b4[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@528e4e8f[Locked by thread http-nio-8084-exec-66] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@528e4e8f[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@6fc88885[Locked by thread http-nio-8084-exec-63] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@6fc88885[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@2c5061c7[Locked by thread http-nio-8084-exec-56] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@2c5061c7[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@2c5061c7[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@2c5061c7[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@55c49da4[Locked by thread http-nio-8084-exec-60] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@55c49da4[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@55c49da4[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@55c49da4[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@3124491e[Locked by thread http-nio-8084-exec-64] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@3124491e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@e10ff6e[Locked by thread http-nio-8084-exec-57] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Transaction:getStatus()
Unlocking ID java.util.concurrent.locks.ReentrantLock@e10ff6e[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (rollback)
<init> Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Locked by thread http-nio-8084-exec-57] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
getTX Locking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f
Unlocking ID java.util.concurrent.locks.ReentrantLock@777655d3[Unlocked] for session 038b5195-d1a2-405a-84e5-022a4af16b3f (commit)
11:21:33.435 [http-nio-8084-exec-68] TRACE org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: com.kobolde.rmm.jar.generic.License.productionProfiles, could not initialize proxy - no Session
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.kobolde.rmm.jar.generic.License.productionProfiles, could not initialize proxy - no Session
Our Transaction class:
public class SyncTransaction extends TransactionImpl {
private static final Map<SessionImpl,SyncTransaction> txMap = new HashMap<>();
private static final ReentrantLock txLock = new ReentrantLock();
public static synchronized SyncTransaction beginTransaction(SessionImpl session) {
SyncTransaction tx = null;
txLock.lock();
try {
if(txMap.containsKey(session)) {
ReentrantLock lock = txMap.get(session).lock;
System.out.println("Waiting on lock ID " + lock.toString());
//HibernateUtil.printCallStack(6, 10);
txLock.unlock();
lock.lock(); // wait for previous transaction to finish
lock.unlock();
txLock.lock();
System.out.println("Done waiting");
}
tx = new SyncTransaction(session);
txMap.put(session, tx);
if(!tx.isActive())
tx.begin();
} finally {
txLock.unlock();
}
return tx;
}
public static SyncTransaction getOpenTransaction(SessionImpl session) {
SyncTransaction ret = null;
try {
txLock.lock();
if(txMap.containsKey(session))
ret = txMap.get(session);
} finally {
txLock.unlock();
}
return ret;
}
public static SyncTransaction getTransaction(SessionImpl session) {
SyncTransaction ret = null;
try {
txLock.lock();
if(txMap.containsKey(session)) {
ret = txMap.get(session);
if(ret.lock.isLocked()) {
System.out.println("Waiting on lock ID " + ret.lock.toString());
//HibernateUtil.printCallStack(6, 10);
txLock.unlock();
ret.lock.lock(); // wait for previous transaction to finish
ret.lock.unlock();
txLock.lock();
System.out.println("Done waiting");
}
System.out.println("getTX Locking ID " + ret.lock.toString() + " for session " + ret.session.getSessionIdentifier());
//HibernateUtil.printCallStack(2, 15);
ret.lock.lock();
}
} finally {
txLock.unlock();
}
return ret != null ? ret : beginTransaction(session);
}
public static void commit(Session session) {
//if(session instanceof SessionImpl) {
SessionImpl ses = (SessionImpl) session;
try {
txLock.lock();
if(txMap.containsKey(ses)) {
SyncTransaction tx = txMap.get(ses);
txLock.unlock();
tx.doCommit();
txLock.lock();
}
} finally {
txLock.unlock();
}
/*} else {
System.out.println("Bad session sent to commit: " + session.getClass().getCanonicalName());
}*/
}
private final ReentrantLock lock = new ReentrantLock();
private final SessionImpl session;
protected SyncTransaction(SessionImpl session) {
super(session.getTransactionCoordinator(), session);
this.session = session;
lock.lock();
System.out.println("<init> Locking ID " + lock.toString() + " for session " + session.getSessionIdentifier());
//HibernateUtil.printCallStack(2, 15);
}
@Override
public TransactionStatus getStatus() {
System.out.println("Transaction:getStatus()");
return super.getStatus();
}
@Override
public void commit() {
try {
/*if(session.isDirty())
session.flush();*/
//super.commit();
} finally {
if(lock.isHeldByCurrentThread()) {
lock.unlock();
System.out.println("Unlocking ID " + lock.toString() + " for session " + session.getSessionIdentifier() + " (commit)");
} else {
//System.out.println("Commit for lock ID " + lock.toString() + " that is not held by me (" + Thread.currentThread().getName() + ")!");
}
}
}
public void doCommit() {
System.out.println("doCommit() for session " + session.getSessionIdentifier());
session.flush();
super.commit();
this.commit();
try {
txLock.lock();
txMap.remove(session, this);
} finally {
txLock.unlock();
}
}
@Override
public void rollback() {
try {
super.rollback();
} finally {
if(lock.isHeldByCurrentThread()) {
lock.unlock();
System.out.println("Unlocking ID " + lock.toString() + " for session " + session.getSessionIdentifier() + " (rollback)");
} else {
//System.out.println("Rollback for lock ID " + lock.toString() + " that is not held by me (" + Thread.currentThread().getName() + ")!");
}
try {
txLock.lock();
txMap.remove(session, this);
} finally {
txLock.unlock();
}
}
}
}
Session proxy, to return our Transaction:
private class SessionOverride implements InvocationHandler {
private final Session parent;
public SessionOverride(Session session) {
parent = session;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
if(method.getName().equals("close")) {
return null;
}
if(method.getName().equals("beginTransaction")) {
return SyncTransaction.beginTransaction((SessionImpl) parent);
}
if(method.getName().equals("getTransaction")) {
return SyncTransaction.getTransaction((SessionImpl) parent);
}
try {
return method.invoke(parent, args);
} catch(Exception e) {
System.out.println("Exception for session ID " + single.getHttpId(parent));
e.printStackTrace();
return null;
}
}
}
Session management in the Factory:
private SessionFactory internalFactory;
private HashMap<String,Session> activeSessions;
private HashMap<Session,Session> wrapSessions;
private HashMap<Session,DelayedSessionClose> timers;
private Timer timer;
private SessionFactoryHeartbeat heartbeat;
private final ReentrantLock sessionLock = new ReentrantLock();
private Session doOpenSession() throws HibernateException {
if(internalFactory == null) {
throw new IllegalStateException("No internal factory has been created");
}
if(!HibernateUtil.useLazy())
return internalFactory.openSession();
String httpSession = null;
try {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
httpSession = attr.getRequest().getSession(false).getId(); // true == allow create
} catch(IllegalStateException e) {
// can't load context, most likely doing unit tests
return internalFactory.openSession();
}
if(httpSession == null || httpSession.isEmpty()) {
return internalFactory.openSession();
}
Session session = activeSessions.get(httpSession);
if(session == null) {
session = internalFactory.openSession();
session.setHibernateFlushMode(FlushMode.MANUAL);
session.setDefaultReadOnly(true);
Session wrap = (Session) Proxy.newProxyInstance(Session.class.getClassLoader(),
new Class[] { Session.class }, new SessionOverride(session));
DelayedSessionClose dsc = new DelayedSessionClose(session, timer);
// we store the original session, with its working close(), and return
// the wrapped version where close() is a no-op.
activeSessions.put(httpSession, session);
wrapSessions.put(session, wrap);
timers.put(session, dsc);
return wrap;
}
timers.get(session).reset(); // tell the watchdog this session has had activity
return wrapSessions.get(session); // return the wrapper
}
I guess I must have messed up somewhere along the way, but I do not see where …