Withheld
Withheld

Reputation: 4693

How to make a static initialization block execute?

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?

Upvotes: 2

Views: 1146

Answers (6)

blalasaadri
blalasaadri

Reputation: 6188

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.

Upvotes: 1

JonK
JonK

Reputation: 2108

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.

Upvotes: 3

ifloop
ifloop

Reputation: 8386

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

Upvotes: 2

David
David

Reputation: 444

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.

Upvotes: 0

Varun Achar
Varun Achar

Reputation: 15109

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

Upvotes: 0

Russell Zahniser
Russell Zahniser

Reputation: 16364

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.

Upvotes: 1

Related Questions