views:

70

answers:

3

I have a Java program using a basic Hibernate session factory. I had an issue with a hibernate hbm.xml mapping file and it crashed my program even though I had the getSessionFactory() call in a try catch

                try
                {
                    session = SessionFactoryUtil.getSessionFactory().openStatelessSession();    
                    session.beginTransaction();
                    rh = getRunHistoryEntry(session);
                    if(rh == null)
                    {
                        throw new Exception("No run history information found in the database for run id " + runId_ + "!");
                    }
                }
                catch(Exception ex)
                {
                    logger.error("Error initializing hibernate");
                }

It still manages to break out of this try/catch and crash the main thread. How do I keep it from doing this? The main issue is I have a bunch of cleanup commands that NEED to be run before the main thread shuts down and need to be able to guarantee that even after a failure it still cleans up and goes down somewhat gracefully. The session factory looks like this:

public class SessionFactoryUtil {
    private static final SessionFactory sessionFactory;  

    static {  
        try 
        {  
            // Create the SessionFactory from hibernate.cfg.xml  
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } 
        catch (Throwable ex) 
        {  
            // Make sure you log the exception, as it might be swallowed  
            System.err.println("Initial SessionFactory creation failed." + ex);  
            throw new ExceptionInInitializerError(ex);  
        }  
    }  

    public static SessionFactory getSessionFactory() 
    {   try
        {
            return sessionFactory;
        }
        catch(Exception ex)
        {
            return null;
        }
    }  
}

The error thrown is the following, and I have fixed it, but I would like to safeguard against any hibernate initializing error from stopping the main thread.

Initial SessionFactory creation failed.org.hibernate.InvalidMappingException: Could not parse mapping document from resource hibernate/TmdIndataLine.hbm.xml
Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.ryanco.db.SessionFactoryUtil.<clinit>(SessionFactoryUtil.java:19)
    at com.ryanco.rta.RtaMain.main(RtaMain.java:148)
Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from resource hibernate/TmdIndataLine.hbm.xml
    at org.hibernate.cfg.Configuration.addResource(Configuration.java:616)
    at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:1635)
    at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:1603)
    at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1582)
    at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1556)
    at org.hibernate.cfg.Configuration.configure(Configuration.java:1476)
    at org.hibernate.cfg.Configuration.configure(Configuration.java:1462)
    at com.ryanco.db.SessionFactoryUtil.<clinit>(SessionFactoryUtil.java:13)
    ... 1 more
Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from input stream
    at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:555)
    at org.hibernate.cfg.Configuration.addResource(Configuration.java:613)
    ... 8 more
Caused by: org.dom4j.DocumentException: Error on line 114 of document  : The element type "class" must be terminated by the matching end-tag "</class>". Nested exception: The element type "class" must be terminated by the matching end-tag "</class>".
    at org.dom4j.io.SAXReader.read(SAXReader.java:482)
    at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:546)
    ... 9 more
+1  A: 

The static initializer in SessionFactoryUtil has a try/catch for all instances of Throwable but your main try/catch block only catches Exceptions. I'd change it to catch Throwables as well and see if that solves the problem.

Steven Paligo
@Steven: Unfortunately it had the same effect.
manyxcxi
@Steven: I read your solution incorrectly. I implemented catching throwable in my main try/catch (like you said) and that solved it. It was a long day.
manyxcxi
@manyxcxi I'm glad that helped. Good luck with your project and thanks.
Steven Paligo
A: 

Well, depending on what type of application you write, static initializers doing anything that reaches outside of the JVM are usually a bad idea.

To the problem: if I read you code correctly, you first catch all Throwables in the static initalizer of SessionFactoryUtil, write an error message to standard error an then throw a new ExceptionInInitializerError yourself, wrapping the catched Throwable in it.

IIRC static initializers are invoked first thing after the class was loaded. The stack trace tells us this happens probably from the main method in class RtaMain, as SessionFactoryUtil.getSessionFactory() seems to be invoked from there, which triggers the class load.

To catch this error you need a try/catch surrounding the call to getSessionFactory() in the main method of your class RtaUtil, catching Throwable, or at least the specific throwable you throw in your static initializer yourself, which is ExceptionInInitializerError.

HTH

Ralf Fischer
A: 

It still manages to break out of this try/catch and crash the main thread.

As mentioned, this bloc in your main method won't catch the ExceptionInInitializerError thrown in your static initializer:

catch(Exception ex) {
    logger.error("Error initializing hibernate")
}

If you want to catch both Exception and Error, you should catch (Throwable t) (by the way, I noticed you said you changed that but I'd like to see what you changed exactly and the error you get.

But to be honest, if the SessionFactory fails at being initialized, I wonder what you're going to do in your main method and it's actually a common practice to let the program fail in that case.

Last thing, the try/catch in this part seems totally useless:

public static SessionFactory getSessionFactory() {
    try {
        return sessionFactory;
    } catch(Exception ex) {
        return null;
    }
} 

You can remove it safely.

Pascal Thivent
@Pascal: Steven was correct, I implemented his solution in the wrong spot in my code initially. The main issue I have with needing to catch everything and close gracefully is that the program updates a status xml file that keeps a number of web apps updated as to what is happening. When I catch a fatal exception the program stops its normal execution path, updates the status file that it is finished but had errors and shuts down. I don't expect to have hibernate issues as it was a typo in an hbm.xml file but I didn't want to leave loose ends.
manyxcxi