2

Searching for an explanation for why my static initialization block wouldn't execute, I found that a static initializer in a class never executes if it references a final.

So I tried to work around this by removing the 'final' from MYWS_PROPS:

public class HibernateUtil {
  public static String MYWS_PROPS = "/myws.properties";

    private static final Logger LOG = Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory = buildSessionFactory();
    private static Properties sProps;

    static {
        try {
            sProps = new Properties();
            sProps.load(ServiceUtils.class.getResourceAsStream(MYWS_PROPS));
            LOG.info("Loaded (from " + MYWS_PROPS + ") connection url: " + sProps.getProperty("dburl"));
        } 
        catch (IOException e) {
            LOG.severe("Cannot find properties file " + MYWS_PROPS);
        }               
    }   



    private static SessionFactory buildSessionFactory() {
        try {
            Configuration config = new Configuration();
            config = config.configure("resources/hibernate.cfg.xml");
            config.setProperty("hibernate.connection.url", sProps.getProperty("dburl"));    // <== NullPointerException!        
            SessionFactory session = config.buildSessionFactory();
            return session;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

}

But that static block still wouldn't execute!

What is the proper way to guarantee execution of a static block?


Update: All answers below mentioned the requirement to reference the class so that the static initialization will run. So I looked again at my stack trace and verified that the class is indeed referenced (otherwise, why would it throw an exception in that very same class method?)

Caused by: java.lang.ExceptionInInitializerError
        at com.corp.dept.proj.myws.HibernateUtil.buildSessionFactory(HibernateUtil.java:76)
        at com.corp.dept.proj.myws.HibernateUtil.<clinit>(HibernateUtil.java:38)
        at com.corp.dept.proj.myws.ServicePortImpl.<init>(ServicePortImpl.java:82)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
        ... 70 more
Caused by: java.lang.NullPointerException
        at com.corp.dept.proj.myws.HibernateUtil.buildSessionFactory(HibernateUtil.java:67)
        ... 77 more

But to be on the safe side, I tried the Class.forName suggestion:

Class.forName("com.corp.dept.proj.myws.HibernateUtil");
mProps.load(ServiceUtils.class.getResourceAsStream(HibernateUtil.MYWS_PROPS));

And that static block still wouldn't execute.

So I tried to explicitly instantiate it:

HibernateUtil justExecTheDarnStaticBlock = new HibernateUtil();
mProps.load(ServiceUtils.class.getResourceAsStream(HibernateUtil.MYWS_PROPS));

But that static block still wouldn't execute!

What am I missing here?

Community
  • 1
  • 1
Withheld
  • 4,603
  • 10
  • 45
  • 76
  • 4
    `I found that a static initializer in a class never executes if it references a final.` This is not true. When do you expect the `static` block to execute? – Sotirios Delimanolis Apr 07 '14 at 15:00
  • Static initialization block is being ran when JVM (class loader - to be specific) loads StaticClass (when it is first time referenced in code). My understanding of the requirement for no final comes from here: http://stackoverflow.com/a/16853836/1864054 What am I missing? – Withheld Apr 07 '14 at 15:03
  • That has to do with `static final` fields which are also constant variables, not with initializer blocks. – Sotirios Delimanolis Apr 07 '14 at 15:04

6 Answers6

3

When dealing with static initalisation, order matters.

Take the following example:

public class StaticTest {
    private static final String someString = initSomeString();
    private static final String someOtherString = initSomeOtherString();
    private static final String whoops;

    static {
        System.out.println("static initializer");
        whoops = "whoops";
    }

    public static void main(String[] args) {
        System.out.println(someOtherString);
    }

    private static String initSomeString() {
        System.out.println("initSomeString");
        return "Initialised";
    }

    private static String initSomeOtherString() {
        System.out.println("initSomeOtherString");
        return whoops + "y daisy";
    }
}

The result of running this class is the following:

initSomeString
initSomeOtherString
static initialiser
nully daisy

Static initialisations occur in the order in which they are declared, meaning that initSomeOtherString() is called before the static initialiser block that initialises whoops. As a result, whoops is still null at that point, so the resulting String is nully daisy and not whoopsy daisy.

If I rearrange the class so that I initialise someOtherString after the static initialiser, I get what I wanted:

public class StaticTest {
    private static final String someString = initSomeString();
    private static final String whoops;

    static {
        System.out.println("static initialiser");
        whoops = "whoops";
    }

    private static final String someOtherString = initSomeOtherString();

    public static void main(String[] args) {
        System.out.println(someOtherString);
    }

    private static String initSomeString() {
        System.out.println("initSomeString");
        return "Initialised";
    }

    private static String initSomeOtherString() {
        System.out.println("initSomeOtherString");
        return whoops + "y daisy";
    }
}

Prints

initSomeString
static initialiser
initSomeOtherString
whoopsy daisy

This is the behaviour that you're running into and is causing your problem. You're initialising sessionFactory by calling buildSessionFactory(), which relies upon sProps being initialised:

config.setProperty("hibernate.connection.url", sProps.getProperty("dburl")
                                               ^ This hasn't been initialised yet

The solution? Move the declaration of sessionFactory to below your static initialiser block.

JonK
  • 2,097
  • 2
  • 25
  • 36
  • 1
    +1 for the great detailed explanation. I should have accepted your answer but @blalasaadri gave me that tip first. SO should let accept more than one answer. :) – Withheld Apr 07 '14 at 16:47
2

The static block will be executed when the class is referenced the first time. (Instantiation or a reference to a static member)

ifloop
  • 8,079
  • 2
  • 26
  • 35
1

If you create an instance of the class the static block will certainly execute.

EDIT: The actual solution which was found in the comments is the following:

The buildSessionFactory() method is used in the initialization of sessionFactory. This depends on the static block having been called already but this isn't the case. If you call sessionFactory = buildSessionFactory(); in the static block rather than in its declaration and after sProps has been initialized the problem will disappear.

blalasaadri
  • 5,990
  • 5
  • 38
  • 58
  • I did just that (see update in my OP) and the static block still wouldn't execute. Any idea why? – Withheld Apr 07 '14 at 16:09
  • 1
    @Withheld You seem to be using the `buildSessionFactory()` method in the initialization of the `sessionFactory` constant. This depends on the static block having been called already; it may be that the order in which this runs is broken. Try calling `sessionFactory = buildSessionFactory();` in the static block rather than in the declaration (and of course after `sProps` has been initialized). – blalasaadri Apr 07 '14 at 16:19
1

The question you link to is just talking about compile-time constants; referencing them does not load the class. But anything you do to load the class -- for example, Class.forName("HibernateUtil") -- will necessarily run the static initializer.

Also, since the static variable you are dealing with is not a primitive type or a String, it can't be a compile-time constant anyway.

Russell Zahniser
  • 16,188
  • 39
  • 30
0

The ClassLoader needs to load the class before it the static block executes. This happens when the class is referenced for the first time.

You could force this by using Class.forName("");. With this, you can use final variables

Varun Achar
  • 14,781
  • 7
  • 57
  • 74
  • It's clear to me that I am missing something very fundamental. See my update above. Additional insights? Thanks. – Withheld Apr 07 '14 at 15:45
0

If you use the class, e.g. calling it's static method, creating an instance of it, the static block will be executed.

Another way is to load the class explicitely by calling Class.forName. This is useful when you only know the name (as a string). Sometimes you have to scan the jar file to get the name and use Class.forname to load it.

David
  • 435
  • 5
  • 7