-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Hibernate 2.1.4 bug: NPE at SessionImpl.java:2451
PostPosted: Thu Aug 19, 2004 6:58 am 
Beginner
Beginner

Joined: Wed Dec 03, 2003 10:59 am
Posts: 47
Hello,

I think this is a Hibernate bug, as it happens sporadically, and results in a NullPointerException:

java.lang.NullPointerException
at
net.sf.hibernate.impl.SessionImpl.flushEntities(SessionImpl.java:2451)
at
net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2256)
at
net.sf.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1801)
at net.sf.hibernate.impl.SessionImpl.getQueries(SessionImpl.java:1567)
at net.sf.hibernate.impl.SessionImpl.find(SessionImpl.java:1532)
at net.sf.hibernate.impl.QueryImpl.list(QueryImpl.java:39)
at
com.example.db.UserUrlDAOHibernate.getUrls(UserUrlDAOHibernate.java:641)


Every few days, randomly, I see this in my logs. Since I get a NPE in Hibernate code, my guess is that the bug is in Hibernate, and that SessionImpl.java:2451 may need some '== null' checks.

Otis

Hibernate version:

2.1.4

Mapping documents:

Code between sessionFactory.openSession() and session.close():

Full stack trace of any exception that occurs:

Name and version of the database you are using:

PostgreSQL 7.3.4

Debug level Hibernate log excerpt:


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 19, 2004 7:36 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Negative.

The way this can happen is if you have shared your session between two threads, or called back into the session during flush.

Since it happens unpredictably, I would bet on a threading problem.

Note that since Hibernate itself is single threaded, and shares very little state between concurrent application threads, "sporadic" bugs are almost certainly NOT Hibernate bugs.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 19, 2004 1:18 pm 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
Otis,
be extremely carefull with this, if you are not well managing your session, your app can damage data... especially if there are a lot of concurrent access.

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 19, 2004 8:11 pm 
Beginner
Beginner

Joined: Wed Dec 03, 2003 10:59 am
Posts: 47
Hm, negative here, too - the application in question is a single-threaded command-line application that creates but a single Hibernate Session.

I'll look into my side, but I'd suggest you look at that NPE, too.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 19, 2004 10:54 pm 
Beginner
Beginner

Joined: Wed Dec 03, 2003 10:59 am
Posts: 47
Hm, could the following be the problem.

My application is a stand-alone, single-threaded, command-line application.
It processes some data for different users in the system, and for each of them it creates a separate Hibernate Session, one at a time, as it goes through users one by one.

While this application is running in 1 JVM, there is another application (a web application) running in a separate JVM. In this web application I also get Hibernate Sessions, one for each logged in user.

It is possible that while a user is logged into the web application (and has his Session in that application's JVM), my stand-alone application creates another Session for the same user.

Could this be causing problems, even though the 2 Sessions are created in completely separate JVMs?


I am also seeing sporadic ClassCastExceptions, like this:

Code:
java.lang.ClassCastException
        at net.sf.hibernate.impl.SessionImpl.flushCollections(SessionImpl.java:2782)
        at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2257)
        at net.sf.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1801)
        at net.sf.hibernate.impl.SessionImpl.getQueries(SessionImpl.java:1567)
        at net.sf.hibernate.impl.SessionImpl.find(SessionImpl.java:1532)
        at net.sf.hibernate.impl.QueryImpl.list(QueryImpl.java:39)


Could this be related?
The first error (NPE) that I posted was in flushEntities method, and this one is in flushCollections, both of which are called from flushEverything, one right after the other, judging from error lines.

If having the same user with 2 Sessions in 2 separate JVMs is a problem, what do you recommend?

Thanks,
Otis


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 19, 2004 11:06 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
The classcast at that line is absolutely, completely safe.

I don't know what you're doing, but i bet there is something very weird about your system that you are not telling us. Like you do some funny serialization of the sessions (perhaps unintentionally), or something like that. Maybe some classloader thing.

Creating two sessions for a "user" is not a problem.

Anyway, I don't think there is any chance of us being able to resolve this from here, since it seems there is something "funny" in your environment.

You can resolve this be reproducing it in a controlled environment.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 20, 2004 9:25 am 
Beginner
Beginner

Joined: Wed Dec 03, 2003 10:59 am
Posts: 47
Hello,

The code in question doesn't really do anything with Hibernate Sessions. No Session sharing between threads, no serialization, etc.

However, there may be something strange with my code that deals with Hibernate Sessions. It is all contained in 1 ServiceLocator class, and all DAO-level methods call a single method to get the same Hibernate Session that is stored in ThreadLocal. Since all execution is done in a single thread, these calls are all sequential. If HibernateException is encountered in the DAO layer, the session is closed and removed from ThreadLocal.

I have tried testing this class, hit it with multiple threads, etc., and I can never break it, even though I'm suspicious about it.

I know you are busy (I'm a Lucene developer, also finishing 'In Action' manuscript for Manning), but you may be able to quickly and easily spot an obvious mistake in this ServiceLocator class.

My DOA layer classes call:

1. To get a Hibernate Session:
public static Session currentSession()

2. If they catch HibernateException:
public static void closeAndRemoveSessionFromThread()


Code:
public class ServiceLocator
{
    /** A cache for the Hibernate <code>Session</code> */
    private static final ThreadLocal SESSION = new ThreadLocal();

    /** A cache for the user Id of the user in the current thread */
    private static final ThreadLocal USER = new ThreadLocal();

    private static final Logger LOG = Logger.getLogger(ServiceLocator.class);

    private static SessionFactory _sesFactory = null;
    private static ServiceLocator _self = null;
    private static Map _userSessions;

    static
    {
        try
        {
            _self = new ServiceLocator();
            // keeps a limited number of user sessions
            // limits their life to 30 min
            // runs expiration check every 5 min
            SimpleFileProperties PROPS = SimpleFileProperties.getInstance();
            LRUMap map = new LRUMap(PROPS.getRequiredIntProperty("user.sessions.cacheSize"));
            _userSessions = new ExpiringMap(map,
                new TimerTTLReferenceHolder(
                    PROPS.getRequiredIntProperty("user.sessions.ttl"),
                    PROPS.getRequiredIntProperty("user.sessions.expirationFreq"))
                );
        }
        catch (Exception e)
        {
            LOG.fatal("Error occurred initializing ServiceLocator: " + e.getMessage());
            throw new ServiceLocatorException("Cannot initialize ServiceLocator", e);
        }
    }

    private ServiceLocator()
        throws HibernateException
    {
            _sesFactory = PersistenceSessionFactory.getSessionFactory();
    }

    /**
     * Returns the Hibernate Session assigned to this thread.
     *
     * @return userInfoId <code>Integer</code> value
     */
    public static Session getSession()
    {
        return (Session) SESSION.get();
    }

    /**
     * Assigns the specified user to the current thread, gets an
     * instance of Hibernate Session, and assigns it to the given
     * user.
     *
     * @param userInfoId an <code>Integer</code> value
     * @exception ServiceLocatorException if an error occurs
     */
    public static void setUser(Integer userInfoId)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Assigning user to this thread: " + userInfoId);
        USER.set(userInfoId);
        Session ses = currentSession();
        if (!_userSessions.containsKey(userInfoId))
        {
            if (LOG.isDebugEnabled())
                LOG.info("Assigning Hibernate Session to user: " + userInfoId);
            _userSessions.put(userInfoId, ses);
        }
    }

    /**
     * Returns the userInfoId of the user assigned to this thread.
     *
     * @return userInfoId <code>Integer</code> value
     */
    public static Integer getUser()
    {
        return (Integer) USER.get();
    }

    /**
     * Assigns the specified user to the current thread, and assigns
     * the given Session to this user.
     *
     * @param userInfoId an <code>Integer</code> value
     * @param ses an instance of Hibernate {@link Session}
     */
    public static void setUser(Integer userInfoId, Session ses)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Assigning user to this thread: " + userInfoId);
        USER.set(userInfoId);
        if (LOG.isDebugEnabled())
            LOG.info("Assigning Hibernate Session to user: " + userInfoId);
        _userSessions.put(userInfoId, ses);
    }

    /**
     * FIXME: update javadoc.
     *
     * @return a <code>Session</code> value
     * @exception ServiceLocatorException if an error occurs
     */
    public static Session currentSession()
    {
        LOG.debug("Getting user assigned to this thread");
        Integer userInfoId = (Integer) USER.get();
        Session ses = null;

        if (userInfoId != null)
        {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Found user assigned to this thread: " + userInfoId);
                LOG.debug("Getting Hibernate Session for user: " + userInfoId);
            }
            ses = (Session) _userSessions.get(userInfoId);
            if ((ses != null) && LOG.isDebugEnabled())
                LOG.debug("Found Hibernate Session for user: " + userInfoId);
        }
        else if (LOG.isDebugEnabled()) {
            LOG.debug("No user assigned to this thread found");
        }

        if (ses == null)
        {
            if (LOG.isDebugEnabled()) {
                LOG.debug("No Hibernate Session found for user: " + userInfoId);
                LOG.debug("Getting Hibernate Session assigned to this thread");
            }
            ses = (Session) SESSION.get();
        }
        if (ses == null)
        {
            if (LOG.isDebugEnabled())
                LOG.debug("No Hibernate Session assigned to this thread found");
            try
            {
                if (LOG.isDebugEnabled())
                    LOG.debug("Opening new Hibernate Session");
                ses = _sesFactory.openSession();
            }
            catch (HibernateException e)
            {
                throw new ServiceLocatorException("Cannot open Hibernate Session", e);
            }
        }
        else
        {
            LOG.debug("Found Hibernate Session assigned to this thread and user, enabling it");
            ses = enableSession(ses);
        }
        SESSION.set(ses);
        return ses;
    }

    /**
     * Disconnects the current Hibernate <code>Session</code>.
     *
     * @exception ServiceLocatorException if an error occurs
     */
    public static void disconnectSession()
    {
        Session ses = (Session) SESSION.get();
        if (ses != null)
        {
            try
            {
                if (ses.isConnected())
                {
                    if (LOG.isDebugEnabled())
                        LOG.debug("Disconnecting Hibernate Session");
                    ses.disconnect();
                }
                else if (LOG.isDebugEnabled()) {
                    LOG.debug("Hibernate Session already disconnected");
                }
            }
            catch (HibernateException e)
            {
                throw new ServiceLocatorException("Cannot close Hibernate Session", e);
            }
        }
        else
        {
            LOG.warn("Tried disconnecting Hibernate Session, but it was not found in this thread");
        }
    }

    /**
     * Removes the Hibernate <code>Session</code> from the current
     * thread.
     */
    public static void removeSessionFromThread()
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Removing Hibernate Session from this thread");
        SESSION.set(null);
    }

    /**
     * Removes the user from the current thread.
     */
    public static void removeUserFromThread()
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Removing User from this thread");
        USER.set(null);
    }

    /**
     * Closes the current Hibernate <code>Session</code>.
     */
    public static void closeSession()
    {
        Session ses = (Session) SESSION.get();
        if (ses != null)
        {
            try
            {
                if (LOG.isDebugEnabled())
                    LOG.debug("Closing Hibernate Session");
                ses.close();
            }
            catch (HibernateException e)
            {
                throw new ServiceLocatorException("Cannot close Hibernate Session", e);
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("Closed Hibernate Session");
            }
        }
        else {
            LOG.warn("Tried closing Hibernate Session, but it was not found in this thread");
        }
    }

    /**
     * Returns a Map containing user sessions.  The keys are
     * userInfoIds, and values are references to the reference holder
     * that holds references to Hibernate Session instances.
     *
     * @return a <code>Map</code> with user sessions
     */
    public static Map getUserSessions()
    {
        return _userSessions;
    }

    /**
     * Closes the current Hibernate <code>Session</code> and removes
     * it from <code>ThreadLocal</code>.  Also removes the Hibernate
     * <code>Session</code> for the user of this thread.
     *
     * @exception ServiceLocatorException if an error occurs
     */
    public static void closeAndRemoveSessionFromThread()
    {
        closeSession();
        removeSessionFromThread();
        if (LOG.isDebugEnabled())
            LOG.debug("Removing Hibernate Session for user: " + getUser());
        _userSessions.remove(getUser());
    }

    private static Session enableSession(Session ses)
    {
        if (ses == null) {
            throw new IllegalArgumentException("Session cannot be null");
        }
        if (!ses.isConnected())
        {
            if (LOG.isDebugEnabled())
                LOG.debug("Hibernate Session is not connected, re-connecting");
            try
            {
                ses.reconnect();
            }
            catch (HibernateException e)
            {
                throw new ServiceLocatorException("Hibernate Session reconnect failed", e);
            }
        }
        return ses;
    }
}


Thank you.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 20, 2004 11:00 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I would focus on your error handling. Probably you somehow manage to get a broken session sticking around in the THreadLocal.

Use a disciplined Interceptor based approach.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.